home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / RELIGION / CONTRAST.ARJ / CONTRAST.C next >
C/C++ Source or Header  |  1991-01-14  |  120KB  |  3,390 lines

  1. /******************************************************************/
  2. /*            contrast - conversion for os/2 of fulcon            */
  3. /* adapted by B.N.H.Scott                                         */
  4. /* from FULCON by Dave Pullin     This Version: 9/1/91            */
  5. /******************************************************************/
  6.  
  7. /* This program contrasts two files by finding shared lines of data,
  8.    and displaying the files with identical sections in the same
  9.    colours.
  10. */
  11.  
  12. /*   The two files, either defined on the command line or in a dialog box, are
  13.    read in, the number of lines counted, and arrays set up. A hash code for each
  14.    line is calculated from its contents, and lines that occur exactly once in
  15.    each file found (these are referred to as 'pins'). Lines that are the same in
  16.    the two files, which are above or below pins, are also counted as pins. All
  17.    other lines are treated as unique.
  18.      A composite file is constructed, which is an attempt to show all
  19.    the lines from both files in the most likely order. Matching blocks of lines
  20.    are kept together; if two blocks of lines have swapped positions in the
  21.    second file, the smaller one is shown twice, above and below the larger one.
  22.    Unique lines are fitted in between the matched blocks in the order they come
  23.    in the real files.
  24.      The two real files may be displayed, or the composite. The text colour
  25.    indicates which lines have been matched, and which are unique. The
  26.    background colour shows either which file the lines belong to (if they are
  27.    unique or have been moved to a different position in the second file), or
  28.    that they are matched, and also not moved.
  29.      A 'bar chart' of the two files is also displayed.
  30. */
  31.  
  32. /* 'Hungarian notation' prefixes used: ht for hash table index
  33.                                        lt for line table index
  34. */
  35.  
  36. /* include headers for Dos, Win & Gpi calls & for string manipulation and
  37.    character type checking
  38. */
  39. #define INCL_DOSMISC
  40. #define INCL_DOSMEMMGR
  41. #define INCL_GPILCIDS
  42. #define INCL_GPIPRIMITIVES
  43. #define INCL_WINBUTTONS
  44. #define INCL_WINDIALOGS
  45. #define INCL_WINENTRYFIELDS
  46. #define INCL_WINERRORS
  47. #define INCL_WINFRAMEMGR
  48. #define INCL_WINHELP
  49. #define INCL_WINHOOKS
  50. #define INCL_WININPUT
  51. #define INCL_WINLISTBOXES
  52. #define INCL_WINMENUS
  53. #define INCL_WINPOINTERS
  54. #define INCL_WINSCROLLBARS
  55. #define INCL_WINSHELLDATA
  56. #define INCL_WINSTATICS
  57. #define INCL_WINSYS
  58. #define INCL_WINWINDOWMGR
  59. #define _MT
  60. #include <string.h>
  61. #include <stdio.h>
  62. #include <ctype.h>
  63. #include <process.h>
  64. #include <os2.h>
  65.  
  66. /* include header defining constants for commands, etc. */
  67. #include "contrast.h"
  68.  
  69. /* macros for accessing huge file in memory, given a linear pointer */
  70. #define MEM(a) MAKEP ((SELECTOROF(a) << usHugeShift) + selBase, OFFSETOF(a))
  71. #define CMEM(a) * (PCH) MEM(a)
  72.  
  73. /* max & min macros */
  74. #define MAX(a,b) ((a) > (b)) ? (a) : (b)
  75. #define MIN(a,b) ((a) < (b)) ? (a) : (b)
  76.  
  77. typedef struct _FONT {
  78.                        FATTRS fattrs;
  79.                        struct _FONT *pfontNext;
  80.                        USHORT usPointSize;
  81.                      } FONT;
  82. typedef FONT *PFONT;
  83.  
  84. /* special structure to stop trap Ds with some file systems;
  85.    allow extra 20 bytes for file name overflow */
  86. typedef struct _MYFILEFINDBUF {
  87.                                 FILEFINDBUF real;
  88.                                 char dummy[20];
  89.                               } MYFILEFINDBUF;
  90.  
  91.  
  92. #define UM_FAIL WM_USER
  93. #define UM_TABLES_MADE WM_USER+1
  94.  
  95. #define COL_UNIQUE        4
  96. #define COL_A_BGRND       5
  97. #define COL_B_BGRND       6
  98. #define COL_MATCHED_BGRND 7
  99.  
  100. /* ids for control windows */
  101. #define ID_VSCROLL 1
  102. #define ID_HSCROLL 2
  103. #define ID_TITLE   3
  104.  
  105. /* local identifier for font */
  106. #define LCID_SELECTED 1L
  107.  
  108. #define STACKSIZE 4096 /* for second thread */
  109.  
  110. /* function declarations */
  111. void cdecl main (int argc, char *argv[]);
  112. void BarChart (HPS hps, LONG cxClient, LONG cyClient, LONG cxBar);
  113. LONG FillBlock (HPS hps, LONG cxClient, LONG cyClient, LONG cxBar,
  114.                 SHORT ltTop, SHORT ltBottom);
  115. LONG PrintableLine (SHORT ltIndex, PLONG plColour, PLONG plBackCol);
  116. SHORT RelLine (SHORT ltBaseLine, SHORT sInc);
  117. MRESULT EXPENTRY ClientWndProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
  118. void ShowError (PSZ string);
  119. void ShowPMError (PSZ string);
  120. PFONT InitialiseFonts (HWND hwnd);
  121. MRESULT EXPENTRY AboutDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
  122. MRESULT EXPENTRY ActualColoursProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
  123. void InitialiseHelp (void);
  124. MRESULT EXPENTRY OpenDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
  125. void FillOpenListBoxes (PSZ szPath, HWND hwndFileList, HWND hwndDirList);
  126. MRESULT EXPENTRY SetColoursProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
  127. void CopyColours (PLONG lFromColour, PLONG lToColour);
  128. USHORT MakeFullPath (PSZ szFileName);
  129. BOOL GetDefaultDir (PSZ szDefDir);
  130. MRESULT EXPENTRY FontsDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
  131. MRESULT EXPENTRY MyEntryFieldProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
  132. PVOID AllocMemPM (USHORT usSize);
  133. void cdecl far MakeTables (void far *dummy);
  134. SHORT CreateTables (void);
  135. void ReleaseMemory (void);
  136. ULONG OpenFile (PSZ szFileName, PHFILE pFileHandle);
  137. SHORT ReadFile (HFILE FileHandle, ULONG ulStartMem, ULONG ulEndMem);
  138. SHORT MakeHashValues (ULONG ulChar, ULONG ulEnd, SHORT ltIndex);
  139. void ChainIdenticalLines (void);
  140. void FindUniquePairs (void);
  141. void ExpandUniquePairs (void);
  142. void SetColours (void);
  143. void Composite (SHORT ltTopA, SHORT ltBottomA, SHORT ltTopB,
  144.                 SHORT ltBottomB, SHORT ltPrecLine, SHORT ltFolLine);
  145. void Add (PSHORT pltIndex, PSHORT pltPrecLine);
  146. PVOID AllocMem (USHORT usSize);
  147.  
  148. /* All global variables are initialised. This is due to a quirk in the MS C6.0
  149.    compiler (and maybe many others); if they aren't initialised, ADDRESSES of
  150.    the variables are put in the data segment, so that a lot of indirection goes
  151.    on during the program. If they are, the values are put in the data segment.
  152.    Can't work out why, myself, but there you go. This gives over 10%
  153.    improvement in speed.
  154. */
  155.  
  156. /* line table arrays */
  157. ULONG *ulLine = NULL;                         /* pointer to text of line in memory */
  158. SHORT *ltLinePair = NULL;                     /* line table indexes of identical line */
  159. SHORT *ltLineNext = NULL, *ltLinePrev = NULL; /* next and previous lines of composite file */
  160. USHORT *usLineCol = NULL, *usBackCol = NULL;  /* colour of line and background */
  161. ULONG *ulLineHash = NULL;                     /* hash code of line */
  162.  
  163. SHORT *HashTable = NULL;                      /* index of line table entries */
  164.  
  165. /****************************DIAGRAM******************************************\
  166.  
  167.   The line tables consist of several arrays, each with one entry for every line
  168. in each file.
  169.   For each line in each file, a hash value is calculated. This is used as an
  170. index for the hash table, which is an array of subscripts for the line tables.
  171. If there is no subscript at that point in the hash table, the subscript of the
  172. current line is entered.
  173.  
  174. HashTable      ltLinePair
  175. ┌───────┐      ┌────────┐
  176. │       │  ┌──>╞════════╡
  177. │       │  │   │        │
  178. │       │  │   │        │
  179. │       │  │   │        │
  180. ╞═══════╡──┘   │        │
  181. │       │      │        │
  182. │       │      │        │
  183. │       │      │        │
  184. │       │      │        │
  185. │       │      │        │
  186. │       │      │        │
  187. │       │      │        │
  188. │       │      │        │
  189. └───────┘      │        │
  190.                └────────┘
  191.  
  192. If there is a subscript present, the hash value of that line (stored in
  193. ulLineHash) is compared with that of the current line. If they don't match, the
  194. computer looks at the next entry in the hash table.
  195.   However, if the hash values match, the computer has found an identical line,
  196. so it changes the entry in the hash table to point to the current line, and
  197. puts the index of the identical line in the ltLinePair entry of the current
  198. line. Thus a chain of identical lines will be built up.
  199.  
  200.  
  201. HashTable      ltLinePair
  202. ┌───────┐      ┌────────┐
  203. │       │      ╞════════╡<────┐
  204. │       │      │        │     │
  205. │       │      │        │     │
  206. │       │      │        │     │
  207. ╞═══════╡──┐   ╞════════╡<──┐─┘
  208. │       │  │   │        │   │
  209. │       │  │   │        │   │
  210. │       │  │   │        │   │
  211. │       │  │   ╞════════╡<┐─┘
  212. │       │  │   │        │ │
  213. │       │  │   │        │ │
  214. │       │  │   │        │ │
  215. │       │  │   │        │ │
  216. └───────┘  └──>╞════════╡─┘
  217.                └────────┘
  218.  
  219. After this has been done for all the lines in the two files, the computer looks
  220. at each entry in the hash table. If an entry points to a pair of lines, one
  221. from each file, the ltLinePair entry for each line is given the other's index.
  222. In any other case (no matching line, two matching lines in the same file, or
  223. more than two matching lines) the chain is broken up totally.
  224.  
  225.  
  226. HashTable      ltLinePair
  227. ┌───────┐      ┌────────┐
  228. │       │    ┌─╞════════╡<┐
  229. ╞═══════╡──┐ │ │        │ │
  230. │       │  │ │ │        │ │
  231. │       │  │ │ │        │ │
  232. │       │  │ │ │        │ │
  233. │       │  └>└>╞════════╡─┘
  234. │       │      │        │
  235. │       │      │        │
  236. │       │    ┌─╞════════╡<┐
  237. │       │    │ │        │ │
  238. │       │    │ │        │ │
  239. │       │    │ │        │ │
  240. ╞═══════╡──┐ │ │        │ │
  241. └───────┘  └>└>╞════════╡─┘
  242.                └────────┘
  243.  
  244.   Lines either side of these 'pins' which match each other (but have not
  245. already been made pins, because there were more than 2 of them) are then made
  246. into pins.
  247.  
  248. \*************************END OF DIAGRAM**************************************/
  249.  
  250. HAB hab = NULL; /* handle to anchor block */
  251.  
  252. PSZ szFile[3] = {NULL, NULL, NULL}; /* names of files, and composite */
  253.  
  254. SHORT sNumLines[3] = {0, 0, 0}; /* number of lines in each file, and composite */
  255. SHORT sTotalLines = 0; /* total number of lines in the two real files */
  256.  
  257. SHORT sMaxLength = 0; /* maximum line length in either file */
  258.  
  259. PFONT pfontHead = NULL; /* head of linked list of fonts */
  260. PFONT pfontSelected = NULL; /* current font */
  261.  
  262. SEL selBase = 0;        /* variables for accessing huge memory for the files */
  263. USHORT usHugeShift = 0;
  264.  
  265. char pWriteLine[512] = ""; /* output buffer */
  266.  
  267. /* user options - loadable and savable - initialise to defaults */
  268. struct {
  269.          LONG lColour[8];
  270.          BOOL fIgnoreLeadingBlanks;
  271.          BOOL fIgnoreTrailingBlanks;
  272.          BOOL fIgnoreAllBlanks;
  273.          BOOL fInterleaveUniqueLines;
  274.          USHORT usPointSize;
  275.          char szFacename[60];
  276.          USHORT fsFontSelection;
  277.        } uoUserOptions =
  278.        {
  279. /* keep up to date with CONTRAST.RC & lDefColour below */
  280.          { CLR_BLUE,       /*   matched text colour        */
  281.            CLR_DARKPINK,   /*      "     "     "           */
  282.            CLR_DARKGREEN,  /*      "     "     "           */
  283.            CLR_DARKCYAN,   /*      "     "     "           */
  284.            CLR_BLACK,      /* unmatched  "     "           */
  285.            CLR_YELLOW,     /* background to file A         */
  286.            CLR_RED,        /*     "      "   "   B         */
  287.            CLR_WHITE       /*     "      "  identical text */
  288.          },
  289.          FALSE,
  290.          FALSE,
  291.          TRUE,
  292.          FALSE,
  293.          0,
  294.          "",
  295.          0
  296.        };
  297.  
  298. /* array of default colours - keep up to date with CONTRAST.RC & user options */
  299. LONG lDefColour[8] = {  CLR_BLUE,
  300.                         CLR_DARKPINK,
  301.                         CLR_DARKGREEN,
  302.                         CLR_DARKCYAN,
  303.                         CLR_BLACK,
  304.                         CLR_YELLOW,
  305.                         CLR_RED,
  306.                         CLR_WHITE
  307.                   };
  308.  
  309. #define FILE_A    0
  310. #define FILE_B    1
  311. #define COMPOSITE 2
  312.  
  313. SHORT file = COMPOSITE; /* file currently displayed */
  314.  
  315. USHORT usMaxPathLength = 0; /* maximum path length allowed by filing system */
  316.  
  317. PFNWP pfnOldEntryFieldProc = NULL;          /* subcalssing of entryfields for */
  318. BOOL fEntryFieldASubclassed = FALSE; /* Open dialog */
  319. BOOL fEntryFieldBSubclassed = FALSE;
  320.  
  321. HWND hwndHelpInstance = NULL; /* handle for help instance */
  322.  
  323. HWND hwndFrame = NULL, hwndClient = NULL; /* handles for main window */
  324.  
  325. /* flags for thread communication */
  326. BOOL fStartTables = TRUE; /* set by the PM thread if the table thread is to start a new
  327.                              set of tables; if not, the table thread will terminate */
  328. BOOL fLoadNewFiles = TRUE; /* set by the PM thread if the user has asked for
  329.                               different files to be used */
  330.  
  331. ULONG ulClearTables = 0;
  332. HSEM hsemClearTables = &ulClearTables; /* semaphore; if 0, the table thread will delete
  333.                                           any old tables, and (depending on
  334.                                           fStartTables) start new ones. At the
  335.                                           beginning of this action, the table thread sets
  336.                                           the semaphore. It periodically checks
  337.                                           the semaphore to see if it has been
  338.                                           cleared by the PM thread (if it has, it
  339.                                           starts again), and when it has
  340.                                           finished the tables, it waits on the
  341.                                           semaphore until it is cleared. It then
  342.                                           starts again. */
  343.  
  344.  
  345. void cdecl main (int argc, char *argv[])
  346. /**********************************************************************/
  347. /* expected arguments:  if argc >= 3,                                 */
  348. /*                         argv[1] = name of file A                   */
  349. /*                         argv[2] = name of file B                   */
  350. /*                      if argc < 3,                                  */
  351. /*                         one or both names are missing              */
  352. /*                                                                    */
  353. /* function : Gets filenames from command line or dialog box. Creates */
  354. /* application window, starts contrasting process, passes messages to */
  355. /* window, and terminates application.                                */
  356. /**********************************************************************/
  357.  
  358. {
  359.     HMQ hmq;
  360.     PSZ szClientClass = "Contrast";
  361.     ULONG flFrameFlags = FCF_STANDARD, ulOptionsSize;
  362.     QMSG qmsg;
  363.     BOOL fCreateWindow;
  364.     USHORT usResult;
  365.     PBYTE Stack;
  366.  
  367.     if (hab = WinInitialize (0))
  368.     {
  369.         if (hmq = WinCreateMsgQueue (hab, 0))
  370.         {
  371.  
  372.             InitialiseHelp ();
  373.  
  374. /* get max path length for file system */
  375.             DosQSysInfo (0, (PBYTE) &usMaxPathLength, 2);
  376.  
  377. /* get user options */
  378.         ulOptionsSize = sizeof(uoUserOptions);
  379.         PrfQueryProfileData (HINI_USERPROFILE, "Contrast", "Options", &uoUserOptions, &ulOptionsSize);
  380.  
  381. /* allocate memory for full file paths */
  382.             if ((szFile[0] = AllocMemPM (usMaxPathLength)) != NULL &&
  383.                 (szFile[1] = AllocMemPM (usMaxPathLength)) != NULL)
  384.             {
  385.  
  386. /* usResult's bits mean as follows:
  387.    bit 0: need Open dialog
  388.    bit 1: couldn't get memory, so exit
  389. */
  390.                 if (argc >= 2)
  391.                 {
  392.                     strcpy (szFile[0], argv[1]);
  393.                     usResult = MakeFullPath (szFile[0]);
  394.                 }
  395.                 else
  396.                 {
  397.                     if (GetDefaultDir (szFile[0]) == TRUE)
  398.                         usResult = 1;
  399.                     else
  400.                         usResult = 2;
  401.                 }
  402.  
  403.                 if (usResult != 2)
  404.                 {
  405.                     if (argc >= 3)
  406.                     {
  407. /* if a directory was specified as the 1st parameter, we'll need the Open dialog */
  408.                         if (szFile[0][strlen (szFile[0]) - 1] == '\\')
  409.                         {
  410.                             usResult = 1;
  411.                         }
  412.  
  413.                         strcpy (szFile[1], argv[2]);
  414.                         usResult |= MakeFullPath (szFile[1]);
  415.                     }
  416.                     else
  417.                     {
  418.                         strcpy (szFile[1], szFile[0]);
  419.                         usResult = 1;
  420.                     }
  421.                 }
  422.  
  423.                 switch (usResult)
  424.                 {
  425.                 case 0:
  426.                     fCreateWindow = TRUE;
  427.                     break;
  428.                 case 1:
  429. /* call the Open dialog - returns FALSE if Cancel button pressed */
  430.                     fCreateWindow = WinDlgBox (HWND_DESKTOP, HWND_DESKTOP, OpenDlgProc,
  431.                                          (HMODULE)NULL, DID_OPEN, NULL);
  432.                     break;
  433.                 default:
  434.                     fCreateWindow = FALSE;
  435.                     break;
  436.                 }
  437.  
  438.                 if (fCreateWindow == TRUE)
  439.                 {
  440.                     if (WinRegisterClass (hab,
  441.                                           szClientClass,
  442.                                           ClientWndProc,
  443.                                           CS_SIZEREDRAW,
  444.                                           0))
  445.                     {
  446.  
  447.                         if (hwndFrame = WinCreateStdWindow (HWND_DESKTOP,
  448.                                                             WS_VISIBLE,
  449.                                                             &flFrameFlags,
  450.                                                             szClientClass,
  451.                                                             NULL,
  452.                                                             0L,
  453.                                                             (HMODULE)NULL,
  454.                                                             ID_CONTRAST,
  455.                                                             &hwndClient))
  456.                         {
  457.  
  458.                             if (WinAssociateHelpInstance (hwndHelpInstance, hwndFrame))
  459.                             {
  460.  
  461.                                 if (Stack = AllocMemPM (STACKSIZE))
  462.                                 {
  463.                                     _beginthread (MakeTables,
  464.                                                   Stack,
  465.                                                   STACKSIZE,
  466.                                                   NULL);
  467.  
  468.                                     while (WinGetMsg (hab, &qmsg, NULL, 0, 0))
  469.                                         WinDispatchMsg (hab, &qmsg);
  470.  
  471. /* tell other thread it can release resources */
  472.                                     fStartTables = FALSE;
  473.                                     DosSemClear (hsemClearTables);
  474.                                 }
  475.                             }
  476.                             WinDestroyWindow (hwndFrame);
  477.                         }
  478.                     }
  479.                 }
  480.             }
  481.  
  482.             WinDestroyHelpInstance (hwndHelpInstance);
  483.             WinDestroyMsgQueue (hmq);
  484.         }
  485.     }
  486.     WinTerminate (hab);
  487.     return;
  488.  
  489. } /* end of main */
  490.  
  491.  
  492.  
  493. void BarChart (HPS hps, LONG cxClient, LONG cyClient, LONG cxBar)
  494. /****************************************************************************/
  495. /* expected parameters: hps = handle for display (input)                    */
  496. /*                      cxClient & cyClient = size of client window (input) */
  497. /*                      cxBar = width of bar (input)                        */
  498. /*                                                                          */
  499. /* function : Draws bar chart.                                              */
  500. /****************************************************************************/
  501.  
  502. {
  503.     SHORT ltTopABlock, ltTopBBlock, ltBottomBBlock, ltIndex, ltPair, ltTopBlock;
  504.     POINTL point;
  505.  
  506.     ltTopABlock = 0;
  507.     ltTopBBlock = ltBottomBBlock = 0;
  508.  
  509.     for (ltIndex = 1; ltIndex <= sNumLines[0] + 1; ltIndex++)
  510.     {
  511.         ltPair = ltLinePair[ltIndex];
  512.         if (ltPair == ltBottomBBlock + 1)
  513.             ltBottomBBlock = ltPair;    /* continuation of block */
  514.         else
  515.         {
  516.             if (ltBottomBBlock != 0)    /* blocks to be coloured */
  517.             {
  518.  
  519. /* draw blocks and connect with line */
  520.                 point.x = cxClient - 4 * cxBar;
  521.                 point.y = FillBlock (hps, cxClient, cyClient, cxBar,
  522.                                         ltTopABlock, ltIndex - 1);
  523.                 GpiMove (hps, &point);
  524.  
  525.                 point.x = cxClient - 2* cxBar;
  526.                 point.y = FillBlock (hps, cxClient, cyClient, cxBar,
  527.                                          ltTopBBlock, ltBottomBBlock);
  528.                 GpiSetColor (hps, uoUserOptions.lColour[usLineCol[ltTopBBlock]]);
  529.                 GpiLine (hps, &point);
  530.  
  531. /* reset bottom of B block, i.e. no block current */
  532.                 ltBottomBBlock = 0;
  533.             }
  534.  
  535.             if (ltPair != 0)            /* new block */
  536.             {
  537.                 ltTopABlock = ltIndex;
  538.                 ltTopBBlock = ltBottomBBlock = ltPair;
  539.             }
  540.         }
  541.     }
  542.  
  543. /* fill blocks for unique lines. This must be done after the matched blocks,
  544.    so that single line changes are always shown. */
  545.     ltTopBlock = 0;
  546.  
  547.     for (ltIndex = 1; ltIndex <= sTotalLines; ltIndex++)
  548.     {
  549.         if (ltIndex == sNumLines[0] + 1 && ltTopBlock != 0)
  550.         {
  551. /* we're at the end of the 1st file, and a block is pending, so fill it */
  552.             FillBlock (hps, cxClient, cyClient, cxBar,
  553.                        ltTopBlock, ltIndex - 1);
  554.  
  555.             ltTopBlock = 0;
  556.         }
  557.  
  558.         if ((ltPair = ltLinePair[ltIndex]) != 0)
  559.         {
  560.             if (ltTopBlock != 0)
  561.             {
  562. /* we've just finished a block of unique lines, so fill it */
  563.                 FillBlock (hps, cxClient, cyClient, cxBar,
  564.                            ltTopBlock, ltIndex - 1);
  565.  
  566.                 ltTopBlock = 0;
  567.             }
  568.         }
  569.         else if (ltTopBlock == 0)
  570.             ltTopBlock = ltIndex;
  571.     }
  572.  
  573.     if (ltTopBlock != 0)
  574. /* we're at the end of the 2nd file, and a block is pending, so fill it */
  575.         FillBlock (hps, cxClient, cyClient, cxBar, ltTopBlock, ltIndex - 1);
  576.  
  577.     return;
  578. } /* end of BarChart () */
  579.  
  580. LONG FillBlock (HPS hps, LONG cxClient, LONG cyClient, LONG cxBar,
  581.                 SHORT ltTop, SHORT ltBottom)
  582. /***************************************************************************/
  583. /* expected parameters: hps = handle for display (input)                   */
  584. /*                      cxClient, cyClient = size of client window (input) */
  585. /*                      cxBar = width of bar (input)                       */
  586. /*                      ltTop, ltBottom = limiting lines of block (input)  */
  587. /* return value : y coordinate of centre of block                          */
  588. /*                                                                         */
  589. /* function : Draws a block in the appropriate column                      */
  590. /***************************************************************************/
  591.  
  592. {
  593.     SHORT sScaler;
  594.     RECTL rectl;
  595.     LONG lBlockColour;
  596.  
  597.     sScaler = MAX (sNumLines[0], sNumLines[1]);
  598.  
  599.     if (ltTop <= sNumLines[0])
  600.     {
  601.         rectl.xLeft = cxClient - 5 * cxBar;
  602.         rectl.xRight = cxClient - 4 * cxBar;
  603.         rectl.yTop = cyClient - (ltTop - 1) * cyClient / sScaler;
  604.         rectl.yBottom = cyClient - ltBottom * cyClient / sScaler - 1;
  605.     }
  606.     else
  607.     {
  608.         rectl.xLeft = cxClient - 2 * cxBar;
  609.         rectl.xRight = cxClient - cxBar;
  610.         rectl.yTop = cyClient - (ltTop - 1 - sNumLines[0]) * cyClient / sScaler;
  611.         rectl.yBottom = cyClient - (ltBottom - sNumLines[0]) * cyClient / sScaler - 1;
  612.     }
  613.  
  614.     if (usLineCol[ltTop] != COL_UNIQUE)
  615.         lBlockColour = uoUserOptions.lColour[usLineCol[ltTop]];
  616.     else
  617.         lBlockColour = uoUserOptions.lColour[usBackCol[ltTop]];
  618.  
  619.     WinFillRect (hps, &rectl, lBlockColour);
  620.  
  621.     return (rectl.yTop + rectl.yBottom ) / 2;
  622.  
  623. } /* end of FillBlock () */
  624.  
  625.  
  626. LONG PrintableLine (SHORT ltIndex, PLONG plColour, PLONG plBackCol)
  627. /***********************************************************************/
  628. /* expected parameters: ltIndex = entry in line table (input)          */
  629. /*                      plColour = colour of line (output)             */
  630. /*                      plBackCol = background colour (output)         */
  631. /* return value: number of characters in line                          */
  632. /*                                                                     */
  633. /* function : Converts a line of text in the file to a printable line, */
  634. /*            taking care of control characters, and tab characters.   */
  635. /***********************************************************************/
  636.  
  637. {
  638.     ULONG ulChar;
  639.     char Char;
  640.     LONG lCount;
  641.     int i;
  642.  
  643.     ulChar = ulLine[ltIndex];
  644.     *plColour = uoUserOptions.lColour[usLineCol[ltIndex]];
  645.     *plBackCol = uoUserOptions.lColour[usBackCol[ltIndex]];
  646.     lCount = 0;
  647.  
  648. /*    while ((Char = CMEM (ulChar)) != 10 && Char != 13 && lCount < 512) */
  649.     while ((Char = CMEM (ulChar)) != '\r' && lCount < 512)
  650.     {
  651.         if (isprint (Char))
  652.             pWriteLine[lCount] = Char;
  653.         else if (Char == '\t')
  654.         {
  655.             for (i = 0; i < 4; i++)
  656.                 pWriteLine[lCount + i] = ' ';
  657.             lCount += 3;
  658.         }
  659.         else
  660.             pWriteLine[lCount] = '?';
  661.  
  662.         ulChar++;
  663.         lCount++;
  664.     }
  665.     return lCount;
  666. } /* end of PrintableLine () */
  667.  
  668.  
  669. SHORT RelLine (SHORT ltBaseLine, SHORT sInc)
  670. /*****************************************************************************/
  671. /* expected parameters: sBaseLine = reference line in composite file         */
  672. /*                      sInc = number of lines forward                       */
  673. /* return value: index of line in line table                                 */
  674. /*                                                                           */
  675. /* function : Finds a line in a real, or composite, file relative to a base. */
  676. /*****************************************************************************/
  677. {
  678.  
  679.     if (file != COMPOSITE)
  680.         return (ltBaseLine + sInc);
  681.  
  682.     else if (sInc > 0)
  683.         while (sInc-- > 0)
  684.             ltBaseLine = ltLineNext[ltBaseLine];
  685.  
  686.     else
  687.         while (sInc++ < 0)
  688.             ltBaseLine = ltLinePrev[ltBaseLine];
  689.  
  690.     return ltBaseLine;
  691. } /* end of RelLine () */
  692.  
  693.  
  694. MRESULT EXPENTRY ClientWndProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
  695. /**********************************************************************/
  696. /* All-purpose window procedure. For functions, see individual cases. */
  697. /**********************************************************************/
  698.  
  699. {
  700.     HPS hps;
  701.     static LONG cxClient, cyClient, cxBar, cyBar, cyTitle;
  702.     RECTL rclInvalid, rclLine, rclScroll, rclBar;
  703.     static LONG cxCaps, cyChar, cyAsc;
  704.     static SHORT sLinesPerPage, sColsPerPage, fTablesValid = 0;
  705.     static SHORT ltTopLine[3], sColPos[3], sFileLine[3];
  706.     static HWND hwndVScroll, hwndHScroll, hwndTitle, hwndMenu;
  707.     LONG lCount, lPrintCol, lBackColour;
  708.     POINTL point;
  709.     SHORT ltIndex, sScaler, sIndex, ltFirstA, ltLastA, ltFirstB, ltLastB;
  710.     SHORT sLineInc, sColInc;
  711.     SHORT sStartPrint, sStopPrint, sPrintLine;
  712.     FONTMETRICS fm;
  713.     PSZ szTitle;
  714.     static HPOINTER hptrWait, hptrArrow;
  715.  
  716.     switch (msg)
  717.     {
  718.     case WM_CREATE:
  719. /******************************************************/
  720. /* function : Initialises variables, fonts, title and */
  721. /*            scroll bar windows, and user options.   */
  722. /******************************************************/
  723.  
  724.         hwndMenu = WinWindowFromID (WinQueryWindow (hwnd, QW_PARENT, FALSE),
  725.                                     FID_MENU);
  726.  
  727.         hptrWait = WinQuerySysPointer (HWND_DESKTOP, SPTR_WAIT, FALSE);
  728.         hptrArrow = WinQuerySysPointer (HWND_DESKTOP, SPTR_ARROW, FALSE);
  729.  
  730.         szFile[COMPOSITE] = "Composite";
  731.  
  732. /* get width & height of system scroll bars */
  733.         cxBar = WinQuerySysValue (HWND_DESKTOP, SV_CXVSCROLL);
  734.         cyBar = WinQuerySysValue (HWND_DESKTOP, SV_CYHSCROLL);
  735.         cyTitle = WinQuerySysValue (HWND_DESKTOP, SV_CYTITLEBAR);
  736.  
  737. /* create scroll bars */
  738.         hwndVScroll = WinCreateWindow (hwnd,
  739.                                        WC_SCROLLBAR,
  740.                                        NULL,
  741.                                        WS_VISIBLE | SBS_VERT,
  742.                                        (SHORT) (cxClient - 7 * cxBar),
  743.                                        (SHORT) cyBar,
  744.                                        (SHORT) cxBar,
  745.                                        (SHORT) (cyClient - cyBar - cyTitle),
  746.                                        hwnd,
  747.                                        HWND_BOTTOM,
  748.                                        ID_VSCROLL,
  749.                                        NULL,
  750.                                        NULL);
  751.  
  752.         hwndHScroll = WinCreateWindow (hwnd,
  753.                                        WC_SCROLLBAR,
  754.                                        NULL,
  755.                                        WS_VISIBLE | SBS_HORZ,
  756.                                        0,
  757.                                        0,
  758.                                        (SHORT) (cxClient - 7 * cxBar),
  759.                                        (SHORT) cyBar,
  760.                                        hwnd,
  761.                                        HWND_BOTTOM,
  762.                                        ID_HSCROLL,
  763.                                        NULL,
  764.                                        NULL);
  765.  
  766.         hwndTitle = WinCreateWindow (hwnd,
  767.                                      WC_STATIC,
  768.                                      szFile[2],
  769.                                      WS_VISIBLE | SS_TEXT | DT_CENTER,
  770.                                      0,
  771.                                      (SHORT) (cyClient - cyTitle),
  772.                                      (SHORT) (cxClient - 6 * cxBar),
  773.                                      (SHORT) cyTitle,
  774.                                      hwnd,
  775.                                      HWND_BOTTOM,
  776.                                      ID_TITLE,
  777.                                      NULL,
  778.                                      NULL);
  779.  
  780. /* find fixed pitch font attributes */
  781.         pfontHead = InitialiseFonts (hwnd);
  782.  
  783. /* set colour of title window */
  784.         WinSetPresParam (hwndTitle, PP_BACKGROUNDCOLORINDEX, 4, &(uoUserOptions.lColour)[COL_A_BGRND+file]);
  785.  
  786. /* set checked status of menu items as appropriate */
  787.         if (uoUserOptions.fIgnoreLeadingBlanks)
  788.             WinSendMsg (hwndMenu,
  789.                         MM_SETITEMATTR,
  790.                         MPFROM2SHORT (MID_IGNORE_LEADING_BLANKS, TRUE),
  791.                         MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED));
  792.  
  793.         if (uoUserOptions.fIgnoreTrailingBlanks)
  794.             WinSendMsg (hwndMenu,
  795.                         MM_SETITEMATTR,
  796.                         MPFROM2SHORT (MID_IGNORE_TRAILING_BLANKS, TRUE),
  797.                         MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED));
  798.  
  799.         if (uoUserOptions.fIgnoreAllBlanks)
  800.             WinSendMsg (hwndMenu,
  801.                         MM_SETITEMATTR,
  802.                         MPFROM2SHORT (MID_IGNORE_ALL_BLANKS, TRUE),
  803.                         MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED));
  804.  
  805.         if (uoUserOptions.fInterleaveUniqueLines)
  806.             WinSendMsg (hwndMenu,
  807.                         MM_SETITEMATTR,
  808.                         MPFROM2SHORT (MID_INTERLEAVE_UNIQUE_LINES, TRUE),
  809.                         MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED));
  810.  
  811.         return 0;
  812.  
  813.     case WM_SIZE:
  814. /**********************************************************************/
  815. /* function : Gets new size of window, and repositions child windows. */
  816. /**********************************************************************/
  817.  
  818.         cxClient = (LONG) SHORT1FROMMP (mp2);
  819.         cyClient = (LONG) SHORT2FROMMP (mp2);
  820.  
  821.         WinSetWindowPos (hwndVScroll,
  822.                          HWND_BOTTOM,
  823.                          (SHORT) (cxClient - 7 * cxBar),
  824.                          (SHORT) cyBar,
  825.                          (SHORT) cxBar,
  826.                          (SHORT) (cyClient - cyBar - cyTitle),
  827.                          SWP_SIZE | SWP_MOVE | SWP_SHOW);
  828.  
  829.         WinSetWindowPos (hwndHScroll,
  830.                          HWND_BOTTOM,
  831.                          0,
  832.                          0,
  833.                          (SHORT) (cxClient - 7 * cxBar),
  834.                          (SHORT) cyBar,
  835.                          SWP_SIZE | SWP_MOVE | SWP_SHOW);
  836.  
  837.         WinSetWindowPos (hwndTitle,
  838.                          HWND_BOTTOM,
  839.                          0,
  840.                          (SHORT) (cyClient - cyTitle),
  841.                          (SHORT) (cxClient - 6 * cxBar),
  842.                          (SHORT) cyTitle,
  843.                          SWP_SIZE | SWP_MOVE | SWP_SHOW);
  844.  
  845.         hps = WinGetPS (hwnd);
  846.  
  847.         if (pfontSelected != NULL)
  848.         {
  849.             if (GpiCreateLogFont (hps, NULL, LCID_SELECTED, &(pfontSelected->fattrs))
  850.                 != FONT_MATCH)
  851.                 ShowPMError ("GpiCreateLogFont");
  852.             if (GpiSetCharSet (hps, LCID_SELECTED) == FALSE)
  853.                 ShowPMError ("GpiSetCharSet");
  854.         }
  855.  
  856.         GpiQueryFontMetrics (hps, (LONG) sizeof (fm), &fm);
  857.         cxCaps = fm.lAveCharWidth;
  858.         cyChar = fm.lMaxBaselineExt + fm.lExternalLeading;
  859.         cyAsc = fm.lLowerCaseAscent;
  860. /*
  861.         if (pfontSelected != NULL)
  862.         {
  863.             GpiSetCharSet (hps, LCID_DEFAULT);
  864.             GpiDeleteSetId (hps, LCID_SELECTED);
  865.         }
  866. */
  867.         WinReleasePS (hps);
  868.  
  869.         sLinesPerPage = (SHORT) ((cyClient - cyTitle - cyBar) / cyChar);
  870.         sColsPerPage = (SHORT) ((cxClient - 7 * cxBar) / cxCaps);
  871.  
  872.         return 0;
  873.  
  874.     case WM_PAINT:
  875. /**********************************************************************/
  876. /* function : Paints any area of the client window when it is needed. */
  877. /**********************************************************************/
  878.  
  879. /* get coordinates of invalidated area, so the minimum of painting is done */
  880.         hps = WinBeginPaint (hwnd, NULL, &rclInvalid);
  881.         GpiErase (hps);
  882.  
  883. /* don't show lines until tables created */
  884.         if (fTablesValid == FALSE)
  885.         {
  886.             WinSetPointer (HWND_DESKTOP, hptrWait);
  887.             WinEndPaint (hps);
  888.             return 0;
  889.         }
  890.  
  891.         WinSetPointer (HWND_DESKTOP, hptrArrow);
  892.  
  893.         if (rclInvalid.xRight > cxClient - 7 * cxBar)
  894.             BarChart (hps, cxClient, cyClient, cxBar);
  895.  
  896. /* set font. Don't bother with error messages - these will have been
  897.    given during WM_CREATE, and would just slow the whole process down.
  898. */
  899.         if (pfontSelected != NULL)
  900.         {
  901.             GpiCreateLogFont (hps, NULL, LCID_SELECTED, &(pfontSelected->fattrs));
  902.             GpiSetCharSet (hps, LCID_SELECTED);
  903.         }
  904.  
  905. /* set x coordinates of background line, and printing */
  906.         rclLine.xLeft = rclInvalid.xLeft;
  907.         rclLine.xRight = MIN (rclInvalid.xRight, cxClient - 7 * cxBar);
  908.         rclInvalid.yTop = MIN (rclInvalid.yTop, cyClient - cyTitle);
  909.         rclInvalid.yBottom = MAX (rclInvalid.yBottom, cyBar);
  910.         point.x = (1 - sColPos[file]) * cxCaps;
  911.  
  912. /* find which lines are to be displayed */
  913.         sStartPrint = (SHORT) ((cyClient - cyTitle - rclInvalid.yTop) / cyChar);
  914.         sStopPrint = (SHORT) (MIN ((cyClient - cyTitle - rclInvalid.yBottom)
  915.                                   / cyChar, sNumLines[file] - sFileLine[file]));
  916.  
  917.         ltIndex = ltTopLine[file];
  918.  
  919.         rclLine.yBottom = cyClient - cyTitle - sStartPrint * cyChar;
  920.  
  921. /* initialise variables for top of bar chart marks */
  922.         ltFirstA = ltFirstB = 0;
  923.  
  924. /* get each line, and print it at appropriate y coordinates */
  925.         for (sPrintLine = 0; sPrintLine <= sLinesPerPage; sPrintLine++)
  926.         {
  927.  
  928. /* adjust variables for top and bottom of bar chart marks */
  929.             if (ltIndex > 0 && ltIndex <= sNumLines[0])
  930.             {
  931.                 if (file == COMPOSITE && usBackCol[ltIndex] == COL_MATCHED_BGRND)
  932.                 {
  933. /* composite lines only come from A, so we must include the checking of B here */
  934.                     if (ltFirstB == 0)
  935.                         ltFirstB = ltLinePair[ltIndex];
  936.                     ltLastB = ltLinePair[ltIndex];
  937.                 }
  938.                 if (ltFirstA == 0)
  939.                     ltFirstA = ltIndex;
  940.                 ltLastA = ltIndex;
  941.             }
  942.             if (ltIndex >sNumLines[0])
  943.             {
  944.                 if (ltFirstB == 0)
  945.                     ltFirstB = ltIndex;
  946.                 ltLastB = ltIndex;
  947.             }
  948.  
  949.             if (sPrintLine >= sStartPrint && sPrintLine <= sStopPrint)
  950.             {
  951.                 lCount = PrintableLine (ltIndex, &lPrintCol, &lBackColour);
  952.                 GpiSetColor (hps, lPrintCol);
  953.                 GpiSetBackColor (hps, lBackColour);
  954.  
  955.                 rclLine.yTop = rclLine.yBottom;
  956.                 rclLine.yBottom = MAX (rclInvalid.yBottom,
  957.                                        rclLine.yBottom - cyChar);
  958.                 point.y = rclLine.yTop - cyAsc;
  959.  
  960.                 GpiCharStringPosAt (hps, &point, &rclLine, CHS_OPAQUE | CHS_CLIP,
  961.                                     lCount, pWriteLine, NULL);
  962.             }
  963.  
  964.             ltIndex = RelLine (ltIndex, 1);
  965.         }
  966. /*
  967.         if (pfontSelected != NULL)
  968.         {
  969.             GpiSetCharSet (hps, LCID_DEFAULT);
  970.             GpiDeleteSetId (hps, LCID_SELECTED);
  971.         }
  972. */
  973.         WinEndPaint (hps);
  974.  
  975. /* adjust scroll bars */
  976.         WinSendMsg (hwndVScroll,
  977.                     SBM_SETSCROLLBAR,
  978.                     MPFROMSHORT (sFileLine[file]),
  979.                     MPFROM2SHORT (1, sNumLines[file] + 1 - sLinesPerPage));
  980.  
  981.         WinSendMsg (hwndVScroll,
  982.                     SBM_SETTHUMBSIZE,
  983.                     MPFROM2SHORT (sLinesPerPage, sNumLines[file]),
  984.                     NULL);
  985.  
  986.         WinSendMsg (hwndHScroll,
  987.                     SBM_SETSCROLLBAR,
  988.                     MPFROMSHORT (sColPos[file]),
  989.                     MPFROM2SHORT (1, sMaxLength + 1 - sColsPerPage));
  990.  
  991.          WinSendMsg (hwndHScroll,
  992.                      SBM_SETTHUMBSIZE,
  993.                      MPFROM2SHORT (sColsPerPage, sMaxLength),
  994.                      NULL);
  995.  
  996. /* draw marks by bar charts to indicate position in file */
  997.         hps = WinGetPS (hwnd);
  998.  
  999. /* erase old marks */
  1000.         rclBar.xLeft = cxClient - 11 * cxBar / 2;
  1001.         rclBar.xRight = cxClient - 21 * cxBar / 4;
  1002.         rclBar.yTop = cyClient;
  1003.         rclBar.yBottom = 0L;
  1004.         WinFillRect (hps, &rclBar, CLR_BACKGROUND);
  1005.  
  1006.         rclBar.xLeft = cxClient - 3 * cxBar / 4;
  1007.         rclBar.xRight = cxClient - cxBar / 2;
  1008.         WinFillRect (hps, &rclBar, CLR_BACKGROUND);
  1009.  
  1010.         sScaler = MAX (sNumLines[0], sNumLines[1]);
  1011.         if (ltFirstA != 0)
  1012.         {
  1013.             rclBar.xLeft = cxClient - 11 * cxBar / 2;
  1014.             rclBar.xRight = cxClient - 21 * cxBar / 4;
  1015.             rclBar.yTop = cyClient - (ltFirstA - 1) * cyClient / sScaler;
  1016.             rclBar.yBottom = cyClient - ltLastA * cyClient / sScaler - 1;
  1017.             WinFillRect (hps, &rclBar, CLR_NEUTRAL);
  1018.         }
  1019.  
  1020.         if (ltFirstB !=0)
  1021.         {
  1022.             rclBar.xLeft = cxClient - 3 * cxBar / 4;
  1023.             rclBar.xRight = cxClient - cxBar / 2;
  1024.             rclBar.yTop = cyClient - (ltFirstB - 1 - sNumLines[0]) * cyClient / sScaler;
  1025.             rclBar.yBottom = cyClient - (ltLastB - sNumLines[0]) * cyClient / sScaler - 1;
  1026.             WinFillRect (hps, &rclBar, CLR_NEUTRAL);
  1027.         }
  1028.  
  1029.         WinReleasePS (hps);
  1030.  
  1031.         return 0;
  1032.  
  1033.  
  1034.     case WM_COMMAND:
  1035. /************************************************/
  1036. /* function : Processes commands from the menu. */
  1037. /************************************************/
  1038.  
  1039.         switch (COMMANDMSG(&msg)->cmd)
  1040.         {
  1041.         case MID_EXIT:
  1042.             WinSendMsg (hwnd, WM_CLOSE, 0L,0L);
  1043.             return 0;
  1044.  
  1045.         case MID_FILEA:
  1046.         case MID_FILEB:
  1047.         case MID_COMPOSITE:
  1048. /* switch file displayed */
  1049.             WinSendMsg (hwndMenu,
  1050.                         MM_SETITEMATTR,
  1051.                         MPFROM2SHORT (file + MID_FILEA, TRUE),
  1052.                         MPFROM2SHORT (MIA_CHECKED, 0));
  1053.  
  1054.             file = COMMANDMSG(&msg)->cmd - MID_FILEA;
  1055.  
  1056.             WinSendMsg (hwndMenu,
  1057.                         MM_SETITEMATTR,
  1058.                         MPFROM2SHORT (file + MID_FILEA, TRUE),
  1059.                         MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED));
  1060.  
  1061.             WinSetWindowText (hwndTitle, szFile[file]);
  1062.  
  1063.             WinSetPresParam (hwndTitle, PP_BACKGROUNDCOLORINDEX, 4, &(uoUserOptions.lColour)[COL_A_BGRND+file]);
  1064.  
  1065.             rclInvalid.xLeft = 0L;
  1066.             rclInvalid.xRight = (LONG) (cxClient - 7 * cxBar);
  1067.             rclInvalid.yTop = cyClient - cyTitle;
  1068.             rclInvalid.yBottom = (LONG) cyBar;
  1069.             WinInvalidateRect (hwnd, &rclInvalid, TRUE);
  1070.  
  1071.             return 0;
  1072.  
  1073.         case MID_ABOUT:
  1074.             WinDlgBox (HWND_DESKTOP, hwndFrame, AboutDlgProc, (HMODULE)NULL, DID_ABOUT, NULL);
  1075.             return 0;
  1076.  
  1077.         case MID_OPEN:
  1078.             if (WinDlgBox (HWND_DESKTOP, hwndFrame, OpenDlgProc,
  1079.                            (HMODULE)NULL, DID_OPEN, NULL) == TRUE)
  1080.             {
  1081.                 fTablesValid = 0;
  1082.                 fLoadNewFiles = TRUE;
  1083.                 DosSemClear (hsemClearTables);
  1084.                 WinSetPointer (HWND_DESKTOP, hptrWait);
  1085.             }
  1086.  
  1087.             return 0;
  1088.  
  1089.         case MID_IGNORE_LEADING_BLANKS:
  1090.             uoUserOptions.fIgnoreLeadingBlanks ^= TRUE;
  1091.             WinSendMsg (hwndMenu,
  1092.                         MM_SETITEMATTR,
  1093.                         MPFROM2SHORT (MID_IGNORE_LEADING_BLANKS, TRUE),
  1094.                         MPFROM2SHORT (MIA_CHECKED,
  1095.                                       uoUserOptions.fIgnoreLeadingBlanks ? MIA_CHECKED : 0));
  1096.  
  1097.             fTablesValid = 0;
  1098.             fLoadNewFiles = FALSE;
  1099.             DosSemClear (hsemClearTables);
  1100.             WinSetPointer (HWND_DESKTOP, hptrWait);
  1101.  
  1102.             return 0;
  1103.  
  1104.         case MID_IGNORE_TRAILING_BLANKS:
  1105.             uoUserOptions.fIgnoreTrailingBlanks ^= TRUE;
  1106.             WinSendMsg (hwndMenu,
  1107.                         MM_SETITEMATTR,
  1108.                         MPFROM2SHORT (MID_IGNORE_TRAILING_BLANKS, TRUE),
  1109.                         MPFROM2SHORT (MIA_CHECKED,
  1110.                                       uoUserOptions.fIgnoreTrailingBlanks ? MIA_CHECKED : 0));
  1111.  
  1112.             fTablesValid = 0;
  1113.             fLoadNewFiles = FALSE;
  1114.             DosSemClear (hsemClearTables);
  1115.             WinSetPointer (HWND_DESKTOP, hptrWait);
  1116.  
  1117.             return 0;
  1118.  
  1119.         case MID_IGNORE_ALL_BLANKS:
  1120.             uoUserOptions.fIgnoreAllBlanks ^= TRUE;
  1121.             WinSendMsg (hwndMenu,
  1122.                         MM_SETITEMATTR,
  1123.                         MPFROM2SHORT (MID_IGNORE_ALL_BLANKS, TRUE),
  1124.                         MPFROM2SHORT (MIA_CHECKED,
  1125.                                       uoUserOptions.fIgnoreAllBlanks ? MIA_CHECKED : 0));
  1126.  
  1127.             fTablesValid = 0;
  1128.             fLoadNewFiles = FALSE;
  1129.             DosSemClear (hsemClearTables);
  1130.             WinSetPointer (HWND_DESKTOP, hptrWait);
  1131.  
  1132.             return 0;
  1133.  
  1134.         case MID_INTERLEAVE_UNIQUE_LINES:
  1135.             uoUserOptions.fInterleaveUniqueLines ^= TRUE;
  1136.             WinSendMsg (hwndMenu,
  1137.                         MM_SETITEMATTR,
  1138.                         MPFROM2SHORT (MID_INTERLEAVE_UNIQUE_LINES, TRUE),
  1139.                         MPFROM2SHORT (MIA_CHECKED,
  1140.                                       uoUserOptions.fInterleaveUniqueLines ? MIA_CHECKED : 0));
  1141.  
  1142.             fTablesValid = 0;
  1143.             fLoadNewFiles = FALSE;
  1144.             DosSemClear (hsemClearTables);
  1145.             WinSetPointer (HWND_DESKTOP, hptrWait);
  1146.  
  1147.             return 0;
  1148.  
  1149.        case MID_SET_COLOURS:
  1150.             if (WinDlgBox (HWND_DESKTOP, hwndFrame, SetColoursProc,
  1151.                            (HMODULE)NULL, DID_COLOURS, NULL) == TRUE)
  1152.             {
  1153.                 WinInvalidateRect (hwnd, NULL, TRUE);
  1154.             }
  1155.             return 0;
  1156.  
  1157.        case MID_SET_FONT:
  1158.             if (WinDlgBox (HWND_DESKTOP, hwndFrame, FontsDlgProc,
  1159.                            (HMODULE)NULL, DID_SET_FONT, NULL) == TRUE)
  1160.             {
  1161.                 WinSendMsg (hwnd,
  1162.                             WM_SIZE,
  1163.                             MPFROM2SHORT ((SHORT) cxClient, (SHORT) cyClient),
  1164.                             MPFROM2SHORT ((SHORT) cxClient, (SHORT) cyClient));
  1165.  
  1166. /* only invalidate text area */
  1167.                 rclInvalid.xLeft = 0L;
  1168.                 rclInvalid.xRight = cxClient - 7 * cxBar;
  1169.                 rclInvalid.yTop = cyClient - cyTitle;
  1170.                 rclInvalid.yBottom = cyBar;
  1171.                 WinInvalidateRect (hwnd, &rclInvalid, TRUE);
  1172.             }
  1173.             return 0;
  1174.  
  1175.        case MID_HELP_FOR_HELP:
  1176.             WinSendMsg (hwndHelpInstance, HM_DISPLAY_HELP, 0L, 0L);
  1177.             return 0;
  1178.  
  1179.         default:
  1180.             break;
  1181.         }
  1182.         break;
  1183.  
  1184.     case HM_QUERY_KEYS_HELP:
  1185.         return (MRESULT) HID_KEYS;
  1186.  
  1187.     case HM_INFORM:
  1188.         WinDlgBox (HWND_DESKTOP, HWND_DESKTOP, ActualColoursProc, (HMODULE)NULL, DID_SHOW_COLOURS, NULL);
  1189.         return 0;
  1190.  
  1191.     case WM_VSCROLL:
  1192. /****************************************************************/
  1193. /*  function : Adjusts place in file using scroll bar messages. */
  1194. /****************************************************************/
  1195.  
  1196.         if (fTablesValid == 0)
  1197.             return 0;
  1198.  
  1199.         switch (SHORT2FROMMP (mp2))
  1200.         {
  1201.         case SB_LINEUP:
  1202.             sLineInc = -1;
  1203.             break;
  1204.         case SB_LINEDOWN:
  1205.             sLineInc = 1;
  1206.             break;
  1207.         case SB_PAGEUP:
  1208.             sLineInc = 1 - sLinesPerPage;
  1209.             break;
  1210.         case SB_PAGEDOWN:
  1211.             sLineInc = sLinesPerPage - 1;
  1212.             break;
  1213.         case SB_SLIDERTRACK:
  1214.             sLineInc = SHORT1FROMMP (mp2) - sFileLine[file];
  1215.             break;
  1216.         default:
  1217.             sLineInc = 0;
  1218.             break;
  1219.         }
  1220.  
  1221.         sLineInc = MAX (1 - sFileLine[file],
  1222.                    MIN (sLineInc,
  1223.                        sNumLines[file] + 1 - sLinesPerPage - sFileLine[file]));
  1224.  
  1225.         if (sLineInc != 0)
  1226.         {
  1227.             sFileLine[file] += sLineInc;
  1228.  
  1229.             ltTopLine[file] = RelLine (ltTopLine[file], sLineInc);
  1230.  
  1231.             rclScroll.xLeft = 0L;
  1232.             rclScroll.xRight = cxClient - 7 * cxBar;
  1233.             rclScroll.yTop = cyClient - cyTitle;
  1234.             rclScroll.yBottom = cyBar;
  1235.             WinScrollWindow (hwnd, 0, (SHORT) (sLineInc * cyChar),
  1236.                              &rclScroll, &rclScroll, NULL, NULL, SW_INVALIDATERGN);
  1237.  
  1238. /* force update of window, since WM_PAINT messages have a low priority,
  1239.    and can get pushed to the back of the queue.
  1240. */
  1241.             WinUpdateWindow (hwnd);
  1242.         }
  1243.  
  1244.         return 0;
  1245.  
  1246.     case WM_HSCROLL:
  1247. /************************************************/
  1248. /* function : Moves view of file left or right. */
  1249. /************************************************/
  1250.  
  1251.         if (fTablesValid == 0)
  1252.             return 0;
  1253.  
  1254.         switch (SHORT2FROMMP (mp2))
  1255.         {
  1256.         case SB_LINELEFT:
  1257.             sColInc = -1;
  1258.             break;
  1259.         case SB_LINERIGHT:
  1260.             sColInc = 1;
  1261.             break;
  1262.         case SB_PAGELEFT:
  1263.             sColInc = -sColsPerPage / 2;
  1264.             break;
  1265.         case SB_PAGERIGHT:
  1266.             sColInc = sColsPerPage / 2;
  1267.             break;
  1268.         case SB_SLIDERTRACK:
  1269.             sColInc = SHORT1FROMMP (mp2) - sColPos[file];
  1270.             break;
  1271.         default:
  1272.             sColInc = 0;
  1273.             break;
  1274.         }
  1275.  
  1276.         sColInc = MAX (1 - sColPos[file],
  1277.                   MIN (sColInc,
  1278.                       (sMaxLength) + 1 - sColsPerPage - sColPos[file]));
  1279.  
  1280.         if (sColInc != 0)
  1281.         {
  1282.             sColPos[file] += sColInc;
  1283.  
  1284.             rclScroll.xLeft = 0L;
  1285.             rclScroll.xRight = cxClient - 7 * cxBar;
  1286.             rclScroll.yTop = cyClient - cyTitle;
  1287.             rclScroll.yBottom = cyBar;
  1288.             WinScrollWindow (hwnd, (SHORT) (-sColInc * cxCaps), 0,
  1289.                              &rclScroll, &rclScroll, NULL, NULL, SW_INVALIDATERGN);
  1290.  
  1291.             WinUpdateWindow (hwnd);
  1292.         }
  1293.  
  1294.         return 0;
  1295.  
  1296.     case WM_CHAR:
  1297. /*********************************************************************/
  1298. /* function : Duplicates scroll bar action with cursor control keys. */
  1299. /*********************************************************************/
  1300.  
  1301. /* PM has same messages for keys as for scroll bars,
  1302.    so they just need to be passed on.
  1303. */
  1304.         if ((SHORT1FROMMP (mp1) & KC_KEYUP) == 0)
  1305.             switch (CHARMSG (&msg) -> vkey)
  1306.             {
  1307.             case VK_UP:
  1308.             case VK_DOWN:
  1309.             case VK_PAGEUP:
  1310.             case VK_PAGEDOWN:
  1311.                 return WinSendMsg (hwndVScroll, msg, mp1, mp2);
  1312.             case VK_LEFT:
  1313.             case VK_RIGHT:
  1314.                 return WinSendMsg (hwndHScroll, msg, mp1, mp2);
  1315.             default:
  1316.                 break;
  1317.             }
  1318.  
  1319.         break;
  1320.  
  1321.     case WM_SAVEAPPLICATION:
  1322.     case WM_CLOSE:
  1323.         if (pfontSelected != NULL)
  1324.         {
  1325.             strcpy (uoUserOptions.szFacename, pfontSelected->fattrs.szFacename);
  1326.             uoUserOptions.usPointSize = pfontSelected->usPointSize;
  1327.             uoUserOptions.fsFontSelection = pfontSelected->fattrs.fsSelection;
  1328.         }
  1329.         PrfWriteProfileData (HINI_USERPROFILE, "Contrast", "Options", &uoUserOptions, sizeof(uoUserOptions));
  1330.         break;
  1331.  
  1332.     case WM_MOUSEMOVE:
  1333.         if (fTablesValid == FALSE)
  1334.         {
  1335.             WinSetPointer (HWND_DESKTOP, hptrWait);
  1336.             return FALSE;
  1337.         }
  1338.         break;
  1339.  
  1340.     case UM_FAIL:
  1341. /*****************************************************************************/
  1342. /* function : Calls Open dialog box if the lookup tables cannot be made.     */
  1343. /*            Restarts program, or ends it, acccording to the user's wishes. */
  1344. /*****************************************************************************/
  1345.  
  1346.         ShowError (pWriteLine);
  1347.         if (WinDlgBox (HWND_DESKTOP, hwndFrame, OpenDlgProc,
  1348.                              (HMODULE)NULL, DID_OPEN, NULL) != TRUE)
  1349.         {
  1350.             fStartTables = FALSE;
  1351.             WinSendMsg (hwnd, WM_CLOSE, 0L, 0L);
  1352.         }
  1353.  
  1354.         fLoadNewFiles = TRUE;
  1355.         DosSemClear (hsemClearTables);
  1356.         return 0;
  1357.  
  1358.     case UM_TABLES_MADE:
  1359. /************************************************************/
  1360. /* function : Sets views of files when tables are complete. */
  1361. /************************************************************/
  1362.  
  1363.         for (sIndex = 0; sIndex < 3; sIndex++)
  1364.         {
  1365.             sFileLine[sIndex] = 1;
  1366.             sColPos[sIndex] = 1;
  1367.         }
  1368.  
  1369.         ltTopLine[0] = 1;
  1370.         ltTopLine[1] = sNumLines[0] + 1;
  1371.         ltTopLine[2] = ltLineNext[0];
  1372.  
  1373.         fTablesValid = 1;
  1374.  
  1375.         szTitle = AllocMemPM (strlen (szFile[0]) + strlen (szFile[1]) + 11);
  1376.         sprintf (szTitle, "Contrast %s %s", szFile[0], szFile[1]);
  1377.  
  1378.         WinSetWindowText (WinQueryWindow (hwnd, QW_PARENT, FALSE), szTitle);
  1379.  
  1380.         DosFreeSeg (SELECTOROF(szTitle));
  1381.  
  1382.         WinInvalidateRect (hwnd, NULL, TRUE);
  1383.  
  1384.         return 0;
  1385.  
  1386.     default:
  1387.         break;
  1388.     }
  1389.  
  1390.     return WinDefWindowProc (hwnd, msg, mp1, mp2);
  1391. } /* end of ClientWndProc() */
  1392.  
  1393.  
  1394.  
  1395.  
  1396. void ShowError(PSZ string)
  1397.  
  1398. {
  1399.     WinMessageBox (HWND_DESKTOP,
  1400.                    hwndFrame,
  1401.                    string,
  1402.                    "Problem",
  1403.                    ID_PROBLEM,
  1404.                    MB_OK | MB_ICONEXCLAMATION);
  1405.  
  1406.     return;
  1407. } /* end of ShowError () */
  1408.  
  1409.  
  1410. void ShowPMError (PSZ string)
  1411.  
  1412. {
  1413.     char buffer[256];
  1414.  
  1415.     sprintf (buffer, "%s error number 0x%x", string, WinGetLastError (hab));
  1416.     ShowError (buffer);
  1417.  
  1418.     return;
  1419. } /* end of ShowPMError () */
  1420.  
  1421.  
  1422. PFONT InitialiseFonts (HWND hwnd)
  1423. /***********************************************************/
  1424. /* expected parameter: hwnd = client window handle (input) */
  1425. /* return value: NULL = no fixed width font found          */
  1426. /*               other = pointer to head of font list      */
  1427. /*                                                         */
  1428. /* function : Gets attributes of fixed width fonts.        */
  1429. /***********************************************************/
  1430.  
  1431. {
  1432.     HPS hps;
  1433.     HDC hdc;
  1434.     FONTMETRICS *pfm;
  1435.     LONG lHorzRes, lVertRes, lRequestFonts, lNumberFonts, lIndex;
  1436.     USHORT usSmallestSize = 64000; /* anything very big */
  1437.     PFONT pfontNew, pfontCurrent = NULL;
  1438.  
  1439.     hps = WinGetPS (hwnd);
  1440.  
  1441. /* get device font resolution, so we can match the font to the display */
  1442.     hdc = GpiQueryDevice (hps);
  1443.     DevQueryCaps (hdc, CAPS_HORIZONTAL_FONT_RES, 1L, &lHorzRes);
  1444.     DevQueryCaps (hdc, CAPS_VERTICAL_FONT_RES, 1L, &lVertRes);
  1445.  
  1446. /* find out number of fonts available */
  1447.     lRequestFonts = 0;
  1448.     lNumberFonts = GpiQueryFonts (hps, QF_PUBLIC, NULL,
  1449.                                   &lRequestFonts, 0L, NULL);
  1450.  
  1451. /* get metrics of fonts */
  1452.     if ((pfm = AllocMemPM ((SHORT) lNumberFonts * sizeof (FONTMETRICS))) == NULL)
  1453.     {
  1454.         WinReleasePS (hps);
  1455.         return NULL;
  1456.     }
  1457.  
  1458.     GpiQueryFonts (hps, QF_PUBLIC, NULL, &lNumberFonts,
  1459.                    (LONG) sizeof (FONTMETRICS), pfm);
  1460.  
  1461.     WinReleasePS (hps);
  1462.  
  1463. /* search for fonts appropriate to display device */
  1464.     for (lIndex = 0; lIndex < lNumberFonts; lIndex++)
  1465.         if (pfm[lIndex].sXDeviceRes == (SHORT) lHorzRes &&
  1466.             pfm[lIndex].sYDeviceRes == (SHORT) lVertRes &&
  1467.             (pfm[lIndex].fsType & FM_TYPE_FIXED))
  1468.         {
  1469.             if ((pfontNew = AllocMemPM (sizeof (FONT))) == NULL)
  1470.                 break;
  1471.  
  1472. /* link to list */
  1473.             pfontNew->pfontNext = pfontCurrent;
  1474.             pfontCurrent = pfontNew;
  1475.  
  1476. /* copy attributes */
  1477.             pfontNew->fattrs.lMatch = pfm[lIndex].lMatch;
  1478.             strcpy (pfontNew->fattrs.szFacename, pfm[lIndex].szFacename);
  1479.             pfontNew->fattrs.idRegistry = pfm[lIndex].idRegistry;
  1480.             pfontNew->fattrs.usCodePage = pfm[lIndex].usCodePage;
  1481.             pfontNew->fattrs.lMaxBaselineExt = pfm[lIndex].lMaxBaselineExt;
  1482.             pfontNew->fattrs.lAveCharWidth = pfm[lIndex].lAveCharWidth;
  1483.             pfontNew->fattrs.usRecordLength = sizeof (FATTRS);
  1484.             pfontNew->fattrs.fsSelection = 0;
  1485.             pfontNew->fattrs.fsType = 0;
  1486.             pfontNew->usPointSize = pfm[lIndex].sNominalPointSize / 10;
  1487. /* speed up printing - tell PM font will not interfere with graphics */
  1488.             pfontNew->fattrs.fsFontUse = FATTR_FONTUSE_NOMIX;
  1489.  
  1490. /* if it's the user's preferred font, use it and stop looking for others */
  1491.             if (!strcmp(pfontNew->fattrs.szFacename, uoUserOptions.szFacename) &&
  1492.                 pfontNew->usPointSize == uoUserOptions.usPointSize)
  1493.             {
  1494.                 pfontSelected = pfontNew;
  1495.                 pfontNew->fattrs.fsSelection = uoUserOptions.fsFontSelection;
  1496.                 usSmallestSize = 0;
  1497.             }
  1498.  
  1499. /* if it's the smallest found yet, use it */
  1500.             if (pfontNew->usPointSize < usSmallestSize)
  1501.             {
  1502.                 pfontSelected = pfontNew;
  1503.                 usSmallestSize = pfontNew->usPointSize;
  1504.             }
  1505.         }
  1506.  
  1507.     DosFreeSeg (SELECTOROF (pfm));
  1508.  
  1509.     return pfontCurrent;
  1510. } /* end of InitFont () */
  1511.  
  1512.  
  1513. MRESULT EXPENTRY AboutDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
  1514. /***************************************************************************/
  1515. /* Simple dialog procedure. Prints some information about the program, and */
  1516. /* exits when the 'OK' button is pressed.                                  */
  1517. /***************************************************************************/
  1518.  
  1519. {
  1520.  
  1521.     switch (msg)
  1522.     {
  1523.     case WM_COMMAND:
  1524.         switch (COMMANDMSG (&msg)->cmd)
  1525.         {
  1526.         case DID_OK:
  1527.             WinDismissDlg (hwnd, TRUE);
  1528.             return 0;
  1529.  
  1530.         default:
  1531.             break;
  1532.         }
  1533.  
  1534.     default:
  1535.         break;
  1536.     }
  1537.  
  1538.     return WinDefDlgProc (hwnd, msg, mp1, mp2);
  1539. }
  1540.  
  1541.  
  1542. MRESULT EXPENTRY ActualColoursProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
  1543. /**********************************************************************/
  1544. /* Displays the actual and default colours used, with an 'OK' button. */
  1545. /**********************************************************************/
  1546.  
  1547. {
  1548.     USHORT usColourIndex;
  1549.     LONG lUserColour;
  1550.     char szString[30];
  1551.  
  1552.     switch (msg)
  1553.     {
  1554.     case WM_INITDLG:
  1555.         for (usColourIndex = 0; usColourIndex < 8; usColourIndex++)
  1556.         {
  1557.             lUserColour = uoUserOptions.lColour[usColourIndex];
  1558.  
  1559.             if (lUserColour == CLR_WHITE)
  1560.             {
  1561.                 lUserColour = 0L;
  1562.             }
  1563.             else if (lUserColour == CLR_BLACK)
  1564.             {
  1565.                 lUserColour = 7L;
  1566.             }
  1567.  
  1568.             WinLoadString (hab, (HMODULE) NULL, SID_POSSIBLE_BASE + (USHORT)lUserColour, 30, szString);
  1569.             WinSetDlgItemText (hwnd, DID_ACTUAL_COLOURS + usColourIndex, szString);
  1570.         }
  1571.         return 0;
  1572.  
  1573.     case WM_COMMAND:
  1574.         switch (COMMANDMSG (&msg)->cmd)
  1575.         {
  1576.         case DID_OK:
  1577.             WinDismissDlg (hwnd, TRUE);
  1578.             return 0;
  1579.  
  1580.         default:
  1581.             break;
  1582.         }
  1583.  
  1584.     default:
  1585.         break;
  1586.     }
  1587.  
  1588.     return WinDefDlgProc (hwnd, msg, mp1, mp2);
  1589. }
  1590.  
  1591.  
  1592. void InitialiseHelp ()
  1593. /****************************************/
  1594. /* function : Sets up help information. */
  1595. /****************************************/
  1596.  
  1597. {
  1598.     HELPINIT helpinit;
  1599.  
  1600.     helpinit.cb = sizeof helpinit;
  1601.     helpinit.pszTutorialName = NULL;
  1602.     helpinit.phtHelpTable = (PHELPTABLE) (0xFFFF0000 | ID_CONTRAST);
  1603.     helpinit.hmodHelpTableModule = (HMODULE)NULL;
  1604.     helpinit.hmodAccelActionBarModule = (HMODULE)NULL;
  1605.     helpinit.idAccelTable = 0;
  1606.     helpinit.idActionBar = 0;
  1607.     helpinit.pszHelpWindowTitle = "Contrast Help Window";
  1608.     helpinit.usShowPanelId = CMIC_HIDE_PANEL_ID;
  1609.     helpinit.pszHelpLibraryName = "contrast.hlp";
  1610.  
  1611.     hwndHelpInstance = WinCreateHelpInstance (hab, &helpinit);
  1612. }
  1613.  
  1614.  
  1615. MRESULT EXPENTRY OpenDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
  1616. /*************************************************************************/
  1617. /* Dialog procedure to get two file names from the user. As close to the */
  1618. /* spirit of CUA as a double file dialog will get.                       */
  1619. /*************************************************************************/
  1620.  
  1621. {
  1622.  
  1623.     PSZ szName, szPath, szEnd, szPath2;
  1624.     HWND hwndFile, hwndDir;
  1625.     USHORT id;
  1626.  
  1627.     switch (msg)
  1628.     {
  1629.     case WM_INITDLG:
  1630.         WinAssociateHelpInstance (hwndHelpInstance, hwnd);
  1631.  
  1632.         if ((szPath = AllocMemPM (usMaxPathLength)) == NULL)
  1633.         {
  1634.             WinSendMsg (hwnd, WM_CLOSE, NULL, NULL);
  1635.             return 0;
  1636.         }
  1637.  
  1638. /* split full path of A into subdirectory & name */
  1639.         szName = strrchr (szFile[0], '\\') + 1;
  1640.         WinSetDlgItemText (hwnd, DID_FILEA, szName);
  1641.         strcpy (szPath, "");
  1642.         strncat (szPath, szFile[0], szName - szFile[0]);
  1643.         WinSetDlgItemText (hwnd, DID_CUR_DIR_A, szPath);
  1644.  
  1645. /* fill listboxes for this subdirectory */
  1646.         hwndFile = WinWindowFromID (hwnd, DID_FILES_A);
  1647.         hwndDir = WinWindowFromID (hwnd, DID_DIRS_A);
  1648.         FillOpenListBoxes (szPath, hwndFile, hwndDir);
  1649.  
  1650. /* do the same for B */
  1651.         szName = strrchr (szFile[1], '\\') + 1;
  1652.         WinSetDlgItemText (hwnd, DID_FILEB, szName);
  1653.         strcpy (szPath, "");
  1654.         strncat (szPath, szFile[1], szName - szFile[1]);
  1655.         WinSetDlgItemText (hwnd, DID_CUR_DIR_B, szPath);
  1656.  
  1657.         hwndFile = WinWindowFromID (hwnd, DID_FILES_B);
  1658.         hwndDir = WinWindowFromID (hwnd, DID_DIRS_B);
  1659.         FillOpenListBoxes (szPath, hwndFile, hwndDir);
  1660.  
  1661.         DosFreeSeg (SELECTOROF(szPath));
  1662.  
  1663.         return 0;
  1664.  
  1665.     case WM_COMMAND:
  1666.         switch (COMMANDMSG (&msg)->cmd)
  1667.         {
  1668.          case DID_OK:
  1669. /* the user wants these files to be contrasted - get the names */
  1670.             if (((szPath = AllocMemPM (usMaxPathLength)) == NULL) ||
  1671.                 ((szPath2 = AllocMemPM (usMaxPathLength)) == NULL) ||
  1672.                 ((szName = AllocMemPM (usMaxPathLength)) == NULL))
  1673.             {
  1674.                 WinDismissDlg (hwnd, FALSE);
  1675.                 return 0;
  1676.             }
  1677.  
  1678.             WinQueryDlgItemText (hwnd, DID_FILEA, usMaxPathLength, szName);
  1679.             if (szName[1] != ':')
  1680.             {
  1681.                 WinQueryDlgItemText (hwnd, DID_CUR_DIR_A, usMaxPathLength, szPath);
  1682.                 strcat (szPath, szName);
  1683.             }
  1684.             else
  1685.             {
  1686.                 strcpy (szPath, szName);
  1687.             }
  1688.  
  1689.             WinQueryDlgItemText (hwnd, DID_FILEB, usMaxPathLength, szName);
  1690.             if (szName[0] == '\0')
  1691.             {
  1692.                 WinQueryDlgItemText (hwnd, DID_FILEA, usMaxPathLength, szName);
  1693.             }
  1694.  
  1695.             if (szName[1] != ':')
  1696.             {
  1697.                 WinQueryDlgItemText (hwnd, DID_CUR_DIR_B, usMaxPathLength, szPath2);
  1698.                 strcat (szPath2, szName);
  1699.             }
  1700.             else
  1701.             {
  1702.                 strcpy (szPath2, szName);
  1703.             }
  1704.  
  1705.  
  1706.             if (stricmp (szPath, szPath2) != 0)
  1707.             {
  1708.                 strcpy (szFile[0], szPath);
  1709.                 strcpy (szFile[1], szPath2);
  1710.                 WinDismissDlg (hwnd, TRUE);
  1711.             }
  1712.  
  1713.             DosFreeSeg (SELECTOROF(szName));
  1714.             DosFreeSeg (SELECTOROF(szPath));
  1715.             DosFreeSeg (SELECTOROF(szPath2));
  1716.  
  1717.             return 0;
  1718.  
  1719.        case DID_CANCEL:
  1720. /* leave dialog with no permanent changes */
  1721.             WinDismissDlg (hwnd, FALSE);
  1722.             return 0;
  1723.  
  1724.         case DID_HELP:
  1725.             WinSendMsg (hwndHelpInstance, HM_EXT_HELP, 0L, 0L);
  1726.             return 0;
  1727.  
  1728.         default:
  1729.             break;
  1730.         }
  1731.  
  1732.     case WM_CONTROL:
  1733.  
  1734.         if (((szPath = AllocMemPM (usMaxPathLength)) == NULL) ||
  1735.             ((szName = AllocMemPM (usMaxPathLength)) == NULL))
  1736.         {
  1737.             WinDismissDlg (hwnd, FALSE);
  1738.             return 0;
  1739.         }
  1740.  
  1741.         switch (SHORT2FROMMP(mp1))
  1742.         {
  1743.         case LN_SELECT: /* same value as EN_SETFOCUS */
  1744.             switch (SHORT1FROMMP(mp1))
  1745.             {
  1746.             case DID_FILES_A:
  1747.             case DID_FILES_B:
  1748. /* set the appropriate file entry field to the current selection */
  1749.                 WinSendMsg ((HWND)mp2,
  1750.                             LM_QUERYITEMTEXT,
  1751.                             MPFROM2SHORT(SHORT1FROMMP(WinSendMsg ((HWND)mp2,
  1752.                                                                   LM_QUERYSELECTION,
  1753.                                                                   MPFROMSHORT(LIT_FIRST),
  1754.                                                                   NULL)),
  1755.                                          usMaxPathLength),
  1756.                             (MPARAM)szName);
  1757.  
  1758.                 id = SHORT1FROMMP(mp1) - DID_FILES_A + DID_FILEA;
  1759.                 WinSetDlgItemText (hwnd, id, szName);
  1760.                 break;
  1761.  
  1762.             case DID_FILEA: /* really EN_SETFOCUS */
  1763. /* if this is the first time here, subclass the field to produce a DID_OK
  1764.    message when enter is pressed, and limit the test length. We do this here
  1765.    because it seems to be the 1st point at which the handle to the field is
  1766.    guaranteed OK. */
  1767.                 if (!fEntryFieldASubclassed)
  1768.                 {
  1769.                     WinSendMsg (HWNDFROMMP(mp2), EM_SETTEXTLIMIT,
  1770.                                 MPFROMSHORT (usMaxPathLength), NULL);
  1771.                     pfnOldEntryFieldProc = WinSubclassWindow (HWNDFROMMP(mp2),
  1772.                                                               MyEntryFieldProc);
  1773.                     fEntryFieldASubclassed = TRUE;
  1774.                 }
  1775.                 break;
  1776.  
  1777.             case DID_FILEB:
  1778. /* subclass this field, and limit it. */
  1779.                 if (!fEntryFieldBSubclassed)
  1780.                 {
  1781.                     WinSendMsg (HWNDFROMMP(mp2), EM_SETTEXTLIMIT,
  1782.                                 MPFROMSHORT (usMaxPathLength), NULL);
  1783.                     WinSubclassWindow (HWNDFROMMP(mp2), MyEntryFieldProc);
  1784.                     fEntryFieldBSubclassed = TRUE;
  1785.                 }
  1786.                 break;
  1787.  
  1788.             default:
  1789.                 break;
  1790.             }
  1791.  
  1792.             break;
  1793.  
  1794.         case LN_ENTER:
  1795.             switch (SHORT1FROMMP(mp1))
  1796.             {
  1797.             case DID_DIRS_A:
  1798.             case DID_DIRS_B:
  1799. /* get the selection */
  1800.                 WinSendMsg ((HWND)mp2,
  1801.                             LM_QUERYITEMTEXT,
  1802.                             MPFROM2SHORT(SHORT1FROMMP(WinSendMsg ((HWND)mp2,
  1803.                                                                   LM_QUERYSELECTION,
  1804.                                                                   MPFROMSHORT(LIT_FIRST),
  1805.                                                                   NULL)),
  1806.                                          usMaxPathLength),
  1807.                             (MPARAM)szPath);
  1808.  
  1809. /* if it's a drive, just get the drive letter from it */
  1810.                 if (szPath[2] == ':')
  1811.                 {
  1812.                     strcpy (szName, "C:\\");
  1813.                     szName[0] = szPath[1];
  1814.                 }
  1815.                 else
  1816.                 {
  1817.                     id = SHORT1FROMMP(mp1) - DID_DIRS_A + DID_CUR_DIR_A;
  1818.                     WinQueryDlgItemText (hwnd, id, usMaxPathLength, szName);
  1819.  
  1820.                     if (strcmp (szPath, "..") == 0)
  1821.                     {
  1822. /* remove last subdirectory; start at the end of the path, skip the null, the
  1823.    last '\', and one character, and change the next '\' to a null */
  1824.                         szEnd = szName + strlen(szName) - 3;
  1825.                         while (*szEnd != '\\')
  1826.                         {
  1827.                             szEnd--;
  1828.                         }
  1829.                         *szEnd = '\0';
  1830.                     }
  1831.                     else
  1832.                     {
  1833. /* add subdirectory to current directory */
  1834.                         strcat (szName, szPath);
  1835.                     }
  1836.  
  1837.                     strcat (szName, "\\");
  1838.                 }
  1839.  
  1840. /* put the directory in the text field */
  1841.                 id = SHORT1FROMMP(mp1) - DID_DIRS_A + DID_CUR_DIR_A;
  1842.                 WinSetDlgItemText (hwnd, id, szName);
  1843.  
  1844. /* refresh the listboxes */
  1845.                 hwndFile = WinWindowFromID (hwnd, SHORT1FROMMP(mp1) - DID_DIRS_A + DID_FILES_A);
  1846.                 hwndDir = WinWindowFromID (hwnd, SHORT1FROMMP(mp1));
  1847.                 WinSendMsg (hwndFile, LM_DELETEALL, NULL, NULL);
  1848.                 WinSendMsg (hwndDir, LM_DELETEALL, NULL, NULL);
  1849.                 FillOpenListBoxes (szName, hwndFile, hwndDir);
  1850.  
  1851. /* use a null string as the starting filename for A; use filename A for B */
  1852.                 if (SHORT1FROMMP(mp1) == DID_DIRS_A)
  1853.                 {
  1854.                     strcpy(szName, "");
  1855.                 }
  1856.                 else
  1857.                 {
  1858.                     WinQueryDlgItemText (hwnd, DID_FILEA, usMaxPathLength, szName);
  1859.                 }
  1860.  
  1861.                 id = SHORT1FROMMP(mp1) - DID_DIRS_A + DID_FILEA;
  1862.                 WinSetDlgItemText (hwnd, id, szName);
  1863.  
  1864.                 break;
  1865.  
  1866.             case DID_FILES_A:
  1867.             case DID_FILES_B:
  1868. /* get the full file paths, and get out of here */
  1869.                 if (((szPath2 = AllocMemPM (usMaxPathLength)) == NULL))
  1870.                 {
  1871.                     WinDismissDlg (hwnd, FALSE);
  1872.                     return 0;
  1873.                 }
  1874.  
  1875.                 WinQueryDlgItemText (hwnd, DID_FILEA, usMaxPathLength, szName);
  1876.                 if (szName[1] != ':')
  1877.                 {
  1878.                     WinQueryDlgItemText (hwnd, DID_CUR_DIR_A, usMaxPathLength, szPath);
  1879.                     strcat (szPath, szName);
  1880.                 }
  1881.                 else
  1882.                 {
  1883.                     strcpy (szPath, szName);
  1884.                 }
  1885.  
  1886.                 WinQueryDlgItemText (hwnd, DID_FILEB, usMaxPathLength, szName);
  1887.                 if (szName[0] == '\0')
  1888.                 {
  1889.                     WinQueryDlgItemText (hwnd, DID_FILEA, usMaxPathLength, szName);
  1890.                 }
  1891.  
  1892.                 if (szName[1] != ':')
  1893.                 {
  1894.                     WinQueryDlgItemText (hwnd, DID_CUR_DIR_B, usMaxPathLength, szPath2);
  1895.                     strcat (szPath2, szName);
  1896.                 }
  1897.                 else
  1898.                 {
  1899.                     strcpy (szPath2, szName);
  1900.                 }
  1901.  
  1902.                 DosFreeSeg (SELECTOROF(szName));
  1903.  
  1904.                 if (stricmp (szPath, szPath2) != 0)
  1905.                 {
  1906.                     strcpy (szFile[0], szPath);
  1907.                     strcpy (szFile[1], szPath2);
  1908.                     WinDismissDlg (hwnd, TRUE);
  1909.                 }
  1910.  
  1911.                 DosFreeSeg (SELECTOROF(szPath2));
  1912.  
  1913.                 break;
  1914.  
  1915.             default:
  1916.                 break;
  1917.             }
  1918.  
  1919.             break;
  1920.  
  1921.         default:
  1922.             break;
  1923.  
  1924.         }
  1925.  
  1926.         DosFreeSeg (SELECTOROF(szPath));
  1927.         DosFreeSeg (SELECTOROF(szName));
  1928.         return 0;
  1929.  
  1930.     case HM_INFORM:
  1931.         WinDlgBox (HWND_DESKTOP, HWND_DESKTOP, ActualColoursProc, (HMODULE)NULL, DID_SHOW_COLOURS, NULL);
  1932.         return 0;
  1933.  
  1934.     default:
  1935.         break;
  1936.     }
  1937.  
  1938.     return WinDefDlgProc (hwnd, msg, mp1, mp2);
  1939. } /* end of OpenDlgProc () */
  1940.  
  1941.  
  1942. void FillOpenListBoxes (PSZ szPath, HWND hwndFileList, HWND hwndDirList)
  1943. /**************************************************************************/
  1944. /* expected parameters: szPath = full path of current directory (input)   */
  1945. /*                      hwndFileList = handle of File listbox (input)     */
  1946. /*                      hwndDirList = handle of Direectory listbox (input)*/
  1947. /**************************************************************************/
  1948.  
  1949. {
  1950.     HDIR hdir = 0xffff;
  1951.     MYFILEFINDBUF FileFindBuf;
  1952.     USHORT cFileNum = 1, usDriveNumber;
  1953.     ULONG ulLogicalDriveMap;
  1954.     char szDrive[5];
  1955.  
  1956. /* DosFindFirst needs a wildcard to search for files */
  1957.     strcat (szPath, "*");
  1958.  
  1959.     if (!DosFindFirst (szPath, &hdir, 0x10, &FileFindBuf, sizeof(FileFindBuf), &cFileNum, 0L))
  1960.     {
  1961.         if (FileFindBuf.real.attrFile & 0x10)
  1962.         {
  1963. /* subdirectory; put in directory listbox */
  1964.             if (strcmp (FileFindBuf.real.achName, "."))
  1965.             {
  1966. /* don't add "." to list */
  1967.                 WinSendMsg (hwndDirList, LM_INSERTITEM,
  1968.                             MPFROMSHORT (LIT_SORTASCENDING), FileFindBuf.real.achName);
  1969.             }
  1970.         }
  1971.         else
  1972.         {
  1973.             WinSendMsg (hwndFileList, LM_INSERTITEM,
  1974.                         MPFROMSHORT (LIT_SORTASCENDING), FileFindBuf.real.achName);
  1975.         }
  1976.  
  1977.         while (!DosFindNext (hdir, &FileFindBuf, sizeof(FileFindBuf), &cFileNum))
  1978.         {
  1979.             if (FileFindBuf.real.attrFile & 0x10)
  1980.             {
  1981.                 if (strcmp (FileFindBuf.real.achName, "."))
  1982.                 {
  1983.                     WinSendMsg (hwndDirList, LM_INSERTITEM,
  1984.                                 MPFROMSHORT (LIT_SORTASCENDING), FileFindBuf.real.achName);
  1985.                 }
  1986.             }
  1987.             else
  1988.             {
  1989.                 WinSendMsg (hwndFileList, LM_INSERTITEM,
  1990.                                    MPFROMSHORT (LIT_SORTASCENDING), FileFindBuf.real.achName);
  1991.             }
  1992.         }
  1993.     }
  1994.  
  1995.     DosFindClose (hdir);
  1996.  
  1997. /* add the drives to the directory listbox */
  1998.     DosQCurDisk (&usDriveNumber, &ulLogicalDriveMap);
  1999.  
  2000.     strcpy (szDrive, "[A:]");
  2001.  
  2002. /* check for each of the 26 possible drives */
  2003.     for (usDriveNumber = 'A'; usDriveNumber < 'Z'; usDriveNumber++)
  2004.     {
  2005.         if ((ulLogicalDriveMap & 1L) == 1)
  2006.         {
  2007. /* drive exists; is it the one currently named in the path? */
  2008.             if (usDriveNumber != szPath[0])
  2009.             {
  2010.                 szDrive[1] = usDriveNumber;
  2011.                 WinSendMsg (hwndDirList, LM_INSERTITEM,
  2012.                             MPFROMSHORT (LIT_SORTASCENDING), szDrive);
  2013.  
  2014.             }
  2015.         }
  2016.  
  2017.         ulLogicalDriveMap = ulLogicalDriveMap >> 1;
  2018.     }
  2019.  
  2020.     return;
  2021.  
  2022. } /* end of FillOpenListBoxes () */
  2023.  
  2024.  
  2025. MRESULT EXPENTRY SetColoursProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
  2026. /*********************************************************/
  2027. /* Dialog procedure to allow the user to specify colours */
  2028. /*********************************************************/
  2029.  
  2030. {
  2031.     LONG lUserColour;
  2032.     static LONG lNewColour[8];
  2033.     USHORT usSampleIndex, usColourSel, usStringIndex;
  2034.     char szString[30], szFontSizeName[60];
  2035.     HWND hwndSample;
  2036.  
  2037.     switch (msg)
  2038.     {
  2039.     case WM_INITDLG:
  2040.         WinAssociateHelpInstance (hwndHelpInstance, hwnd);
  2041.  
  2042. /* put current colours in candidate colours */
  2043.         CopyColours (uoUserOptions.lColour, lNewColour);
  2044.  
  2045. /* put use-of-colour names in listbox */
  2046.         for (usStringIndex = 0; usStringIndex < 8; usStringIndex++)
  2047.         {
  2048.             WinLoadString (hab, (HMODULE) NULL, SID_USED_BASE + usStringIndex, 30, szString);
  2049.             WinSendDlgItemMsg (hwnd, DID_COLOURS_USED, LM_INSERTITEM,
  2050.                                MPFROMSHORT (LIT_END), szString);
  2051.         }
  2052.  
  2053.         WinSendDlgItemMsg (hwnd, DID_COLOURS_USED, LM_SELECTITEM,
  2054.                            (MPARAM)0, (MPARAM)TRUE);
  2055.  
  2056. /* The list of available colours is, due to the strange design of PM, itself strange.
  2057.    For most colours, their position in the listbox gives their index. However, CLR_WHITE
  2058.    and CLR_BLACK have negative indices, so they have been put at positions 0 and 7
  2059.    respectively. If I used CLR_BACKGROUND and CLR_NEUTRAL the coding would be simpler,
  2060.    but changing the default colours in the Control Panel would affect them, and I don't
  2061.    want that (nor do I want to try to explain it to a user). It thus depends on the
  2062.    colour constants staying the same.
  2063. */
  2064.  
  2065. /* put possible-colour names in listbox */
  2066.         for (usStringIndex = 0; usStringIndex < 16; usStringIndex++)
  2067.         {
  2068.             WinLoadString (hab, (HMODULE) NULL, SID_POSSIBLE_BASE + usStringIndex, 30, szString);
  2069.             WinSendDlgItemMsg (hwnd, DID_POSSIBLE_COLOURS, LM_INSERTITEM,
  2070.                            MPFROMSHORT (LIT_END), szString);
  2071.         }
  2072.  
  2073. /* set the selection of the possible-colours listbox */
  2074.         lUserColour = lNewColour[0];
  2075.  
  2076.         if (lUserColour == CLR_WHITE)
  2077.         {
  2078.             lUserColour = 0L;
  2079.         }
  2080.         else if (lUserColour == CLR_BLACK)
  2081.         {
  2082.             lUserColour = 7L;
  2083.         }
  2084.  
  2085.         WinSendDlgItemMsg (hwnd, DID_POSSIBLE_COLOURS, LM_SELECTITEM,
  2086.                            MPFROMLONG(lUserColour), (MPARAM)TRUE);
  2087.  
  2088. /* set foreground & background of sample texts, & the font */
  2089.         sprintf (szFontSizeName, "%u.%s", pfontSelected->usPointSize,
  2090.                                           pfontSelected->fattrs.szFacename);
  2091.  
  2092.         for (usSampleIndex = 0; usSampleIndex < 14; usSampleIndex++)
  2093.         {
  2094.  
  2095.             hwndSample = WinWindowFromID (hwnd, DID_F1_BA + usSampleIndex);
  2096.  
  2097.             WinSetPresParam (hwndSample,
  2098.                              PP_FOREGROUNDCOLORINDEX,
  2099.                              4,
  2100.                              &lNewColour[usSampleIndex / 3]);
  2101.  
  2102.             WinSetPresParam (hwndSample,
  2103.                              PP_BACKGROUNDCOLORINDEX,
  2104.                              4,
  2105.                              &lNewColour[usSampleIndex % 3 + 5]);
  2106.  
  2107.             WinSetPresParam (hwndSample,
  2108.                              PP_FONTNAMESIZE,
  2109.                              sizeof (szFontSizeName),
  2110.                              szFontSizeName);
  2111.  
  2112.         }
  2113.  
  2114.         return 0;
  2115.  
  2116.     case WM_COMMAND:
  2117.         switch (COMMANDMSG (&msg)->cmd)
  2118.         {
  2119.         case DID_OK:
  2120. /* use the candidate colours */
  2121.             CopyColours (lNewColour, uoUserOptions.lColour);
  2122.  
  2123.             WinDismissDlg (hwnd, TRUE);
  2124.             return 0;
  2125.  
  2126.         case DID_CANCEL:
  2127. /* exit with no changes */
  2128.             WinDismissDlg (hwnd, FALSE);
  2129.             return 0;
  2130.  
  2131.         case DID_HELP:
  2132.  
  2133.             WinSendMsg (hwndHelpInstance, HM_EXT_HELP, 0L, 0L);
  2134.             return 0;
  2135.  
  2136.         case DID_DEFAULT:
  2137. /* put the defaults to the candidate colours */
  2138.             CopyColours (lDefColour, lNewColour);
  2139.  
  2140. /* reset the selection of the possible-colours listbox */
  2141.             lUserColour = lNewColour[SHORT1FROMMR(WinSendDlgItemMsg (hwnd,
  2142.                                                                     DID_COLOURS_USED,
  2143.                                                                     LM_QUERYSELECTION,
  2144.                                                                     NULL,
  2145.                                                                     NULL))];
  2146.  
  2147.             if (lUserColour == CLR_WHITE)
  2148.             {
  2149.                 lUserColour = 0L;
  2150.             }
  2151.             else if (lUserColour == CLR_BLACK)
  2152.             {
  2153.                 lUserColour = 7L;
  2154.             }
  2155.  
  2156.             WinSendDlgItemMsg (hwnd, DID_POSSIBLE_COLOURS, LM_SELECTITEM,
  2157.                                MPFROMLONG(lUserColour), (MPARAM)TRUE);
  2158.  
  2159. /* reset the colours of the sample texts */
  2160.         for (usSampleIndex = 0; usSampleIndex < 14; usSampleIndex++)
  2161.         {
  2162.             hwndSample = WinWindowFromID (hwnd, DID_F1_BA + usSampleIndex);
  2163.  
  2164.             WinSetPresParam (hwndSample,
  2165.                              PP_FOREGROUNDCOLORINDEX,
  2166.                              4,
  2167.                              &lNewColour[usSampleIndex / 3]);
  2168.  
  2169.             WinSetPresParam (hwndSample,
  2170.                              PP_BACKGROUNDCOLORINDEX,
  2171.                              4,
  2172.                              &lNewColour[usSampleIndex % 3 + 5]);
  2173.         }
  2174.  
  2175.             return 0;
  2176.  
  2177.         default:
  2178.             break;
  2179.         }
  2180.  
  2181.     case WM_CONTROL:
  2182.         switch (SHORT2FROMMP(mp1))
  2183.         {
  2184.         case LN_ENTER:
  2185. /* exit using the newly-set colours */
  2186.             CopyColours (lNewColour, uoUserOptions.lColour);
  2187.  
  2188.             WinDismissDlg (hwnd, TRUE);
  2189.             break;
  2190.  
  2191.         case LN_SELECT:
  2192.  
  2193.             if (SHORT1FROMMP(mp1) == DID_COLOURS_USED)
  2194.             {
  2195. /* reset the selection of the possible-colours listbox */
  2196.                 lUserColour = lNewColour[SHORT1FROMMR(WinSendMsg (mp2,
  2197.                                                              LM_QUERYSELECTION,
  2198.                                                              NULL,
  2199.                                                              NULL))];
  2200.  
  2201.                 if (lUserColour == CLR_WHITE)
  2202.                 {
  2203.                     lUserColour = 0L;
  2204.                 }
  2205.                 else if (lUserColour == CLR_BLACK)
  2206.                 {
  2207.                     lUserColour = 7L;
  2208.                 }
  2209.  
  2210.                 WinSendDlgItemMsg (hwnd, DID_POSSIBLE_COLOURS, LM_SELECTITEM,
  2211.                                    MPFROMLONG(lUserColour), (MPARAM)TRUE);
  2212.             }
  2213.             else if (SHORT1FROMMP(mp1) == DID_POSSIBLE_COLOURS)
  2214.             {
  2215. /* set the selected used-colour to the selected possible-colour */
  2216.                 lUserColour = (LONG)WinSendMsg (mp2, LM_QUERYSELECTION, NULL, NULL);
  2217.  
  2218.                 if (lUserColour == 0L)
  2219.                 {
  2220.                     lUserColour = CLR_WHITE;
  2221.                 }
  2222.                 else if (lUserColour == 7L)
  2223.                 {
  2224.                     lUserColour = CLR_BLACK;
  2225.                 }
  2226.  
  2227.                 usColourSel = SHORT1FROMMR(WinSendDlgItemMsg (hwnd,
  2228.                                                          DID_COLOURS_USED,
  2229.                                                          LM_QUERYSELECTION,
  2230.                                                          NULL,
  2231.                                                          NULL));
  2232.  
  2233.                 lNewColour[usColourSel] = lUserColour;
  2234.  
  2235. /* reset the colours of the affected samples */
  2236.                 if (usColourSel > 4)
  2237.                 {
  2238.                     for (usSampleIndex = usColourSel - 5;
  2239.                          usSampleIndex < 14;
  2240.                          usSampleIndex += 3)
  2241.                     {
  2242.                         WinSetPresParam (WinWindowFromID (hwnd, DID_F1_BA + usSampleIndex),
  2243.                                          PP_BACKGROUNDCOLORINDEX,
  2244.                                          4,
  2245.                                          &lNewColour[usColourSel]);
  2246.                     }
  2247.                 }
  2248.                 else
  2249.                 {
  2250.                     for (usSampleIndex = usColourSel * 3;
  2251.                          usSampleIndex < (usColourSel + 1) * 3 - usColourSel / 4;
  2252.                          usSampleIndex++)
  2253.                     {
  2254.                         WinSetPresParam (WinWindowFromID (hwnd, DID_F1_BA + usSampleIndex),
  2255.                                          PP_FOREGROUNDCOLORINDEX,
  2256.                                          4,
  2257.                                          &lNewColour[usColourSel]);
  2258.                     }
  2259.  
  2260.                 }
  2261.             }
  2262.  
  2263.             break;
  2264.  
  2265.         default:
  2266.             break;
  2267.         }
  2268.  
  2269.         return NULL;
  2270.  
  2271.     case HM_INFORM:
  2272.         WinDlgBox (HWND_DESKTOP, HWND_DESKTOP, ActualColoursProc, (HMODULE)NULL, DID_SHOW_COLOURS, NULL);
  2273.         return 0;
  2274.  
  2275.     default:
  2276.         break;
  2277.     }
  2278.  
  2279.     return WinDefDlgProc (hwnd, msg, mp1, mp2);
  2280. }
  2281.  
  2282.  
  2283. void CopyColours (PLONG lFromColour, PLONG lToColour)
  2284. /**********************************************/
  2285. /* copies one set of colour values to another */
  2286. /**********************************************/
  2287.  
  2288. {
  2289.     USHORT usColIndex;
  2290.  
  2291.     for (usColIndex = 0; usColIndex < 8; usColIndex++)
  2292.     {
  2293.         lToColour[usColIndex] = lFromColour[usColIndex];
  2294.     }
  2295.  
  2296. }
  2297.  
  2298.  
  2299.  
  2300. USHORT MakeFullPath (PSZ szFileName)
  2301. /*****************************************************************************/
  2302. /* expected parameter : szFileName = file name to be expanded (input/output) */
  2303. /* return value: 0 = success                                                 */
  2304. /*               1 = no such file or directory                               */
  2305. /*               2 = failure (no memory)                                     */
  2306. /*                                                                           */
  2307. /* function : expands file name to a full directory path, including drive    */
  2308. /*****************************************************************************/
  2309.  
  2310. {
  2311.     PSZ szParse, szTemp, szCurPath;
  2312.     USHORT usDisk, usAttr;
  2313.     ULONG ulDriveMap;
  2314.  
  2315.     if ((szCurPath = AllocMemPM (usMaxPathLength)) == NULL ||
  2316.         (szTemp = AllocMemPM (usMaxPathLength)) == NULL)
  2317.         return 2;
  2318.  
  2319. /* convert to upper case - looks better, since OS/2 returns paths in upper
  2320.    case, and makes drive calculation easier */
  2321.     szParse = strupr(szFileName);
  2322.  
  2323. /* if a drive was supplied, use it; if not, use the current one */
  2324.     if (szParse[1] == ':')
  2325.     {
  2326.         usDisk = *szParse - 'A' + 1;
  2327.         szParse += 2;
  2328.     }
  2329.     else
  2330.     {
  2331.         DosQCurDisk (&usDisk, &ulDriveMap);
  2332.     }
  2333.  
  2334. /* if the path isn't specified from the root directory, use the current one */
  2335.     if (*szParse != '\\')
  2336.     {
  2337.         DosQCurDir (usDisk, szTemp, &usMaxPathLength);
  2338.         sprintf (szCurPath, "\\%s",szTemp);
  2339.  
  2340. /* don't add backslash if either current directory or rest of parameter are null */
  2341.         if (*szTemp != '\0' && *szParse != '\0')
  2342.             strcat (szCurPath, "\\");
  2343.     }
  2344.     else
  2345.     {
  2346.         strcpy (szCurPath, "");
  2347.     }
  2348.  
  2349.     strcpy (szTemp, szParse);
  2350.     sprintf (szFileName, "%c:%s%s", usDisk + 'A' - 1, szCurPath, szTemp);
  2351.  
  2352.     DosFreeSeg (SELECTOROF (szCurPath));
  2353.  
  2354. /* remove any "\dir\.." found in the string */
  2355.     while ((szParse = strstr (szFileName, "..")) != NULL)
  2356.     {
  2357. /* save rest of string after ".." */
  2358.         strcpy (szTemp, szParse + 2);
  2359.  
  2360. /* go back to before preceding '\' & 1 character */
  2361.         szParse -= 3;
  2362.  
  2363. /* look for preceding '\' and change to null character to truncate string */
  2364.         while (*szParse != '\\')
  2365.             szParse--;
  2366.         *szParse = '\0';
  2367.  
  2368. /* add rest of string */
  2369.         strcat (szFileName, szTemp);
  2370.     }
  2371.  
  2372.     DosFreeSeg (SELECTOROF (szTemp));
  2373.  
  2374. /* if the user has specified a sub-directory, return at once */
  2375.     if (szFileName[strlen (szFileName) - 1] == '\\')
  2376.     {
  2377.         return 0;
  2378.     }
  2379.  
  2380. /* If it is just a path, append a backslash. */
  2381.  
  2382. /* get file attributes */
  2383.     if (DosQFileMode (szFileName, &usAttr, 0L))
  2384. /* we can't find it, so default to the current directory, & say we need
  2385.    the dialog */
  2386.     {
  2387.         sprintf (pWriteLine,"Couldn't find %s",szFileName);
  2388.         ShowError (pWriteLine);
  2389.         if (GetDefaultDir (szFileName))
  2390.             return 1;
  2391.         else
  2392.             return 2;
  2393.     }
  2394.     else
  2395.     {
  2396. /* check to see if subdirectory */
  2397.         if (usAttr & 0x10)
  2398.         {
  2399.             strcat (szFileName, "\\");
  2400.         }
  2401.     }
  2402.  
  2403.     return 0;
  2404. } /* end of MakeFullPath () */
  2405.  
  2406.  
  2407. BOOL GetDefaultDir (PSZ szDefDir)
  2408. /*************************************************************/
  2409. /* expected parameter: szDefDir = current directory (output) */
  2410. /* return value: TRUE = success                              */
  2411. /*               FALSE = failure (no memory)                 */
  2412. /*************************************************************/
  2413.  
  2414. {
  2415.  
  2416.     USHORT usDisk;
  2417.     ULONG ulDriveMap;
  2418.     PSZ szDir;
  2419.  
  2420.     if ((szDir = AllocMemPM (usMaxPathLength)) == NULL)
  2421.     {
  2422.         return FALSE;
  2423.     }
  2424.  
  2425.     DosQCurDisk (&usDisk, &ulDriveMap);
  2426.     DosQCurDir (usDisk, szDir, &usMaxPathLength);
  2427.     sprintf (szDefDir, "%c:\\%s", usDisk - 1 + 'A', szDir);
  2428.     if (szDir[0] != '\0')
  2429.     {
  2430.         strcat (szDefDir, "\\");
  2431.     }
  2432.  
  2433.     DosFreeSeg (SELECTOROF(szDir));
  2434.     return TRUE;
  2435. } /* end of GetDefaultDir () */
  2436.  
  2437.  
  2438.  
  2439. MRESULT EXPENTRY FontsDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
  2440. /***************************************************************************/
  2441. /* allows different fonts to be selected                                   */
  2442. /***************************************************************************/
  2443.  
  2444. {
  2445.     static PFONT pfontNew;
  2446.     char szFontName[60];
  2447.     int iFontSel, iFontIndex;
  2448.  
  2449.     switch (msg)
  2450.     {
  2451.     case WM_INITDLG:
  2452.         WinAssociateHelpInstance (hwndHelpInstance, hwnd);
  2453.  
  2454.         iFontSel = 0;
  2455.         iFontIndex = 0;
  2456.  
  2457. /* walk list of fonts (created in InitialiseFonts ()) to get font names and
  2458.    sizes, and select the current font in the listbox */
  2459.  
  2460.         for (pfontNew = pfontHead; pfontNew != NULL; pfontNew = pfontNew->pfontNext)
  2461.         {
  2462.             sprintf (szFontName, "%s %u", pfontNew->fattrs.szFacename,
  2463.                                           pfontNew->usPointSize);
  2464.  
  2465.             WinSendDlgItemMsg (hwnd, DID_FONTS_AVAILABLE, LM_INSERTITEM,
  2466.                            MPFROMSHORT (LIT_END), szFontName);
  2467.  
  2468.             if (pfontNew == pfontSelected)
  2469.             {
  2470.                 iFontSel = iFontIndex;
  2471.             }
  2472.             iFontIndex++;
  2473.  
  2474.         }
  2475.  
  2476.         WinSendDlgItemMsg (hwnd,
  2477.                            DID_FONTS_AVAILABLE,
  2478.                            LM_SELECTITEM,
  2479.                            MPFROMSHORT(iFontSel),
  2480.                            (MPARAM)TRUE);
  2481.  
  2482. /* from here on in the dialog, pfontNew is the candidate font */
  2483.         pfontNew = pfontSelected;
  2484.  
  2485.         return 0;
  2486.  
  2487.     case WM_COMMAND:
  2488.         switch (COMMANDMSG (&msg)->cmd)
  2489.         {
  2490.         case DID_OK:
  2491. /* set current font to new one */
  2492.             pfontSelected = pfontNew;
  2493.  
  2494.             WinDismissDlg (hwnd, TRUE);
  2495.             return 0;
  2496.  
  2497.         case DID_CANCEL:
  2498. /* return without changing the font */
  2499.             WinDismissDlg (hwnd, FALSE);
  2500.             return 0;
  2501.  
  2502.         case DID_HELP:
  2503.  
  2504.             WinSendMsg (hwndHelpInstance, HM_EXT_HELP, 0L, 0L);
  2505.             return 0;
  2506.  
  2507.         default:
  2508.             break;
  2509.         }
  2510.  
  2511.     case WM_CONTROL:
  2512.         switch (SHORT2FROMMP(mp1))
  2513.         {
  2514. /* PM assigns the same value (1) to LN_SELECT and BN_CLICKED, both of which
  2515.    we want to respond to, so we use one case statement for both, and check
  2516.    the ID of the control (which we really ought to do anyway) */
  2517.  
  2518.         case LN_SELECT:
  2519.             if (SHORT1FROMMP(mp1) == DID_FONTS_AVAILABLE)
  2520.             {
  2521. /* this really is a LN_SELECT message from the listbox, so find which item
  2522.    was selected */
  2523.                 iFontSel = SHORT1FROMMR (WinSendMsg (mp2,
  2524.                                                      LM_QUERYSELECTION,
  2525.                                                      NULL,
  2526.                                                      NULL));
  2527.  
  2528. /* walk through the linked list of fonts to the correct one */
  2529.                 pfontNew = pfontHead;
  2530.  
  2531.                 for (iFontIndex = 0; iFontIndex < iFontSel; iFontIndex++)
  2532.                 {
  2533.                     pfontNew = pfontNew->pfontNext;
  2534.                 }
  2535.  
  2536. /* set the state of the bold & italic checkboxes to those last requested for
  2537.    this font */
  2538.                 WinSendDlgItemMsg (hwnd,
  2539.                                    DID_BOLD,
  2540.                                    BM_SETCHECK,
  2541.                                    MPFROMSHORT ((pfontNew->fattrs.fsSelection & FATTR_SEL_BOLD) == FATTR_SEL_BOLD),
  2542.                                    NULL);
  2543.  
  2544.                 WinSendDlgItemMsg (hwnd,
  2545.                                    DID_ITALIC,
  2546.                                    BM_SETCHECK,
  2547.                                    MPFROMSHORT ((pfontNew->fattrs.fsSelection & FATTR_SEL_ITALIC) == FATTR_SEL_ITALIC),
  2548.                                    NULL);
  2549.             }
  2550.             else
  2551.             {
  2552. /* this is really a BN_CLICKED message; toggle the state of the font
  2553.    characteristics, according to which checkbox it was */
  2554.                 if (SHORT1FROMMP(mp1) == DID_BOLD)
  2555.                     pfontNew->fattrs.fsSelection ^= FATTR_SEL_BOLD;
  2556.                 else
  2557.                     pfontNew->fattrs.fsSelection ^= FATTR_SEL_ITALIC;
  2558.  
  2559. /* toggle the state of the checkbox */
  2560.                 WinSendDlgItemMsg (hwnd,
  2561.                                    SHORT1FROMMP(mp1),
  2562.                                    BM_SETCHECK,
  2563.                                    MPFROMSHORT(SHORT1FROMMR(WinSendDlgItemMsg (hwnd,
  2564.                                                                                SHORT1FROMMP(mp1),
  2565.                                                                                BM_QUERYCHECK,
  2566.                                                                                NULL,
  2567.                                                                                NULL)) ^ 1),
  2568.                                    NULL);
  2569.             }
  2570.             break;
  2571.  
  2572.         default:
  2573.             break;
  2574.         }
  2575.  
  2576.         return NULL;
  2577.  
  2578.     case HM_INFORM:
  2579.         WinDlgBox (HWND_DESKTOP, HWND_DESKTOP, ActualColoursProc, (HMODULE)NULL, DID_SHOW_COLOURS, NULL);
  2580.         return 0;
  2581.  
  2582.     default:
  2583.         break;
  2584.     }
  2585.  
  2586.     return WinDefDlgProc (hwnd, msg, mp1, mp2);
  2587. } /* end of FontsDlgProc () */
  2588.  
  2589.  
  2590. MRESULT EXPENTRY MyEntryFieldProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
  2591. /**************************************************************************/
  2592. /* subclasses the entry fields for the Open dialog, so a return causes a  */
  2593. /* DID_OK message. This is because we can't use a DEFPUSHBUTTON, since it */
  2594. /* ends the dialog when return is pressed in the directory list.          */
  2595. /**************************************************************************/
  2596. {
  2597.     if (msg == WM_CHAR &&
  2598.         (SHORT1FROMMP(mp1) & (KC_VIRTUALKEY | KC_KEYUP)) == KC_VIRTUALKEY &&
  2599.         (SHORT2FROMMP(mp2) == VK_ENTER) || (SHORT2FROMMP(mp2) == VK_NEWLINE))
  2600.     {
  2601.         WinPostMsg (WinQueryWindow (hwnd, QW_PARENT, 0),
  2602.                     WM_COMMAND,
  2603.                     MPFROMSHORT (DID_OK),
  2604.                     MPFROM2SHORT (CMDSRC_PUSHBUTTON, FALSE));
  2605.     }
  2606.     return pfnOldEntryFieldProc (hwnd, msg, mp1, mp2);
  2607. }
  2608.  
  2609.  
  2610. PVOID AllocMemPM (USHORT usSize)
  2611. /* allocates memory for PM thread */
  2612.  
  2613. {
  2614.     SEL Selector;
  2615.     char buffer[24];
  2616.  
  2617.     if (DosAllocSeg (usSize, &Selector, 0))
  2618.     {
  2619.         strcpy (buffer, "can't allocate memory");
  2620.         ShowError (buffer);
  2621.         return NULL;
  2622.     }
  2623.     return MAKEP (Selector, 0);
  2624.  
  2625. } /* end of AllocMemPM () */
  2626.  
  2627.  
  2628.  
  2629. /************************************************************/
  2630. /* The following functions are in the second (table) thread */
  2631. /************************************************************/
  2632.  
  2633. void cdecl far MakeTables (void far *dummy)
  2634. {
  2635.     while (fStartTables == TRUE)
  2636.     {
  2637.         DosSemSet (hsemClearTables);
  2638.  
  2639.         switch (CreateTables ())
  2640.         {
  2641.         case 0:
  2642.             WinPostMsg (hwndClient, UM_FAIL, NULL, NULL);
  2643.             break;
  2644.  
  2645.         case 1:
  2646.             WinPostMsg (hwndClient, UM_TABLES_MADE, NULL, NULL);
  2647.             break;
  2648.  
  2649.         default:
  2650.             break;
  2651.         }
  2652.  
  2653.         DosSemWait (hsemClearTables, -1L);
  2654.  
  2655.         ReleaseMemory ();
  2656.     }
  2657.  
  2658.     _endthread ();
  2659. }
  2660.  
  2661.  
  2662. SHORT CreateTables (void)
  2663. /***********************************************************************/
  2664. /* return value: 0 = failure                                           */
  2665. /*               1 = success                                           */
  2666. /*               2 = interrupted                                       */
  2667. /*                                                                     */
  2668. /* function: Reads in files to memory and creates hash and line tables */
  2669. /***********************************************************************/
  2670.  
  2671. {
  2672.     HFILE hFileA, hFileB; /* files opened by OpenFile, closed by ReadFile */
  2673.     static ULONG ulFileLengthA, ulFileLengthB, ulStartFileB, ulTotalLength;
  2674.     SHORT sArraySize;
  2675.     PSZ szDefFileName;
  2676.  
  2677.     if (fLoadNewFiles == TRUE)
  2678.     {
  2679. /* Check if second file is just a path, i.e. the user wants to use the
  2680.    same filename as the first file. If so, extract & append the filename. */
  2681.         if (szFile[1][strlen (szFile[1]) - 1] == '\\')
  2682.         {
  2683.             szDefFileName = strrchr (szFile[0], '\\') + 1;
  2684.             strcat (szFile[1], szDefFileName);
  2685.         }
  2686.  
  2687.         if ((ulFileLengthA = OpenFile (szFile[0], &hFileA)) == 0L)
  2688.             return 0;
  2689.  
  2690. /* work out space needed, including gap so second file is aligned on a
  2691.    32768 byte boundary
  2692. */
  2693.     ulTotalLength = ulStartFileB = (ulFileLengthA / 32768 + 1) * 32768;
  2694.  
  2695.         if ((ulFileLengthB = OpenFile (szFile[1], &hFileB)) == 0L)
  2696.             return 0;
  2697.  
  2698.         ulTotalLength += ulFileLengthB;
  2699.  
  2700.         if (DosAllocHuge ((USHORT) (ulTotalLength / 65536L),
  2701.                           (USHORT) (ulTotalLength % 65536L),
  2702.                           &selBase,
  2703.                           0,
  2704.                           0))
  2705.         {
  2706.             strcpy (pWriteLine, "can't allocate memory for files");
  2707.             return 0;
  2708.         }
  2709.  
  2710. /* get selector shift for access to huge memory */
  2711.         DosGetHugeShift (&usHugeShift);
  2712.  
  2713.         if (ReadFile (hFileA, 0L, ulFileLengthA) == 0)
  2714.         {
  2715.             strcat (pWriteLine, szFile[0]);
  2716.             return 0;
  2717.         }
  2718.  
  2719.         if (ReadFile (hFileB, ulStartFileB, ulTotalLength) == 0)
  2720.         {
  2721.             strcat (pWriteLine, szFile[1]);
  2722.             return 0;
  2723.         }
  2724.     }
  2725.  
  2726. /* allocate full 64k for start-of-line and hash-value arrays. We will shrink
  2727.    them to the correct size later. This means the files are only read once. In
  2728.    future, if we haven't enough memory for this, we should count the line and
  2729.    try to reallocate, so small comparisons might fit in. If we do, we should
  2730.    let the user know we're trying to use less memory */
  2731.  
  2732.     if (   (ulLine = AllocMem (0)) == NULL
  2733.         || (ulLineHash = AllocMem (0)) == NULL)
  2734.         return 0;
  2735.  
  2736.     sMaxLength = 0;
  2737.  
  2738.     if (DosSemWait (hsemClearTables, 0L) == 0)
  2739.         return 2;
  2740.  
  2741.     sNumLines[0] = MakeHashValues (0L, ulFileLengthA, 1);
  2742.  
  2743.     if (DosSemWait (hsemClearTables, 0L) == 0)
  2744.         return 2;
  2745.  
  2746.     sTotalLines = MakeHashValues (ulStartFileB, ulTotalLength, sNumLines[0] + 1);
  2747.  
  2748.     if (DosSemWait (hsemClearTables, 0L) == 0)
  2749.         return 2;
  2750.  
  2751.     sNumLines[1] = sTotalLines - sNumLines[0];
  2752.  
  2753.  
  2754. /* limit maximum length to 512, since PM won't print longer strings */
  2755.     sMaxLength = MIN (sMaxLength, 512);
  2756.  
  2757.     if (sTotalLines >= 16384)
  2758.     {
  2759.         strcpy (pWriteLine, "too many lines - must be less than 16K in total");
  2760.         return 0;
  2761.     }
  2762.  
  2763.     sArraySize = sTotalLines + 1;
  2764.  
  2765. /************************************************************/
  2766. /* reallocate memory for line hash and start of line tables */
  2767. /************************************************************/
  2768.     DosReallocSeg (sArraySize * sizeof (ULONG), SELECTOROF (ulLine));
  2769.     DosReallocSeg (sArraySize * sizeof (ULONG), SELECTOROF (ulLineHash));
  2770.  
  2771. /* Allocate memory for other tables */
  2772.  
  2773.     if (   (ltLinePair = AllocMem (sArraySize * sizeof (SHORT))) == NULL
  2774.         || (ltLinePrev = AllocMem (sArraySize * sizeof (SHORT))) == NULL
  2775.         || (ltLineNext = AllocMem (sArraySize * sizeof (SHORT))) == NULL
  2776.         || (usLineCol = AllocMem (sArraySize * sizeof (USHORT))) == NULL
  2777.         || (usBackCol = AllocMem (sArraySize * sizeof (USHORT))) == NULL
  2778.         || (HashTable = AllocMem (sTotalLines * sizeof (SHORT))) == NULL)
  2779.         return 0;
  2780.  
  2781.     ChainIdenticalLines ();
  2782.  
  2783.     if (DosSemWait (hsemClearTables, 0L) == 0)
  2784.         return 2;
  2785.  
  2786.     FindUniquePairs ();
  2787.  
  2788.     DosFreeSeg (SELECTOROF (HashTable));
  2789.  
  2790.     if (DosSemWait (hsemClearTables, 0L) == 0)
  2791.         return 2;
  2792.  
  2793.     ExpandUniquePairs ();
  2794.  
  2795.     DosFreeSeg (SELECTOROF (ulLineHash));
  2796.  
  2797.     if (DosSemWait (hsemClearTables, 0L) == 0)
  2798.         return 2;
  2799.  
  2800.     SetColours ();
  2801.  
  2802.     if (DosSemWait (hsemClearTables, 0L) == 0)
  2803.         return 2;
  2804.  
  2805. /* make composite file */
  2806.     sNumLines[2] = 0;
  2807.     Composite (1, sNumLines[0], sNumLines[0] + 1, sTotalLines, 0, 0);
  2808.  
  2809.     return 1;
  2810. } /* end of CreateTables () */
  2811.  
  2812.  
  2813. void ReleaseMemory (void)
  2814. {
  2815.     if (fLoadNewFiles == TRUE)
  2816.         DosFreeSeg (selBase);
  2817.  
  2818.     DosFreeSeg (SELECTOROF (ulLine));
  2819.     DosFreeSeg (SELECTOROF (ltLinePrev));
  2820.     DosFreeSeg (SELECTOROF (ltLineNext));
  2821.     DosFreeSeg (SELECTOROF (ltLinePair));
  2822.     DosFreeSeg (SELECTOROF (usLineCol));
  2823.     DosFreeSeg (SELECTOROF (usBackCol));
  2824.  
  2825. } /* end of ReleaseMemory () */
  2826.  
  2827.  
  2828. ULONG OpenFile (PSZ szFileName, PHFILE pFileHandle)
  2829. /***********************************************************************/
  2830. /* expected parameters : szFileName = name of file (input)             */
  2831. /*                       pFileHandle = pointer to file handle (output) */
  2832. /* return value:     0 = failure                                       */
  2833. /*               other = length of file in bytes                       */
  2834. /***********************************************************************/
  2835.  
  2836. {
  2837.     USHORT usAction;
  2838.     FILESTATUS FileInfo;
  2839.  
  2840.     if (DosOpen (szFileName,
  2841.                  pFileHandle,
  2842.                  &usAction,             /* file exists/created/replaced  */
  2843.                  0L,                    /* file size (ignored)           */
  2844.                  1,                     /* attributes (read only)        */
  2845.                  1,                     /* open if exists, fail if not   */
  2846.                  32,                    /* deny write, ask for read-only */
  2847.                  0L))                   /* reserved                      */
  2848.     {
  2849.         strcpy (pWriteLine, "failed to open ");
  2850.         strcat (pWriteLine, szFileName);
  2851.         return 0L;
  2852.     }
  2853.  
  2854. /* get file length from file information */
  2855.     if (DosQFileInfo (*pFileHandle,
  2856.                       1,                    /* level of info   */
  2857.                       (PCH) &FileInfo,      /* buffer for info */
  2858.                       sizeof FileInfo ))    /* size of buffer  */
  2859.     {
  2860.         strcpy (pWriteLine, "failed to get information for ");
  2861.         strcat (pWriteLine, szFileName);
  2862.         return 0L;
  2863.     }
  2864.  
  2865.     return FileInfo.cbFile;
  2866.  
  2867. } /* end of OpenFile () */
  2868.  
  2869.  
  2870. SHORT ReadFile (HFILE FileHandle, ULONG ulStartMem, ULONG ulEndMem)
  2871. /***********************************************************************/
  2872. /* expected parameters : FileHandle = handle of file (input)           */
  2873. /*                       ulStartMem = start of memory for file (input) */
  2874. /*                       ulEndMem   = end of memory for file (input)   */
  2875. /* return value : 0 = failure                                          */
  2876. /*                1 = success                                          */
  2877. /*                                                                     */
  2878. /* function : reads in a file and closes it                            */
  2879. /***********************************************************************/
  2880.  
  2881. {
  2882.     USHORT usBytesRequested = 32768, usBytesRead;
  2883.  
  2884. /********************************************************************/
  2885. /* read in file in blocks half a segment long, so no overlap occurs */
  2886. /********************************************************************/
  2887.     do
  2888.     {
  2889.         if (ulEndMem < ulStartMem + 32768)
  2890.         {
  2891.             usBytesRequested = ulEndMem - ulStartMem;
  2892.         }
  2893.  
  2894.         if (DosRead (FileHandle, (PVOID)MEM (ulStartMem), usBytesRequested, &usBytesRead))
  2895.         {
  2896.             strcpy (pWriteLine, "error in reading ");
  2897.             DosClose (FileHandle);
  2898.             return 0;
  2899.         }
  2900.  
  2901.         ulStartMem += usBytesRead;
  2902.     } while (usBytesRead != 0);
  2903.  
  2904.     DosClose (FileHandle);
  2905.  
  2906.     return 1;
  2907.  
  2908. } /* end of ReadFile () */
  2909.  
  2910. SHORT MakeHashValues (ULONG ulChar, ULONG ulEnd, SHORT ltIndex)
  2911. /******************************************************************/
  2912. /* expected parameters : ulChar = start of file in memory (input) */
  2913. /*                       ulEnd = end of file in memory (input)    */
  2914. /*                       ltIndex = index of first line (input)    */
  2915. /* return value : index of last line in file                      */
  2916. /*                                                                */
  2917. /* function : sets up tables for starts of lines and hash values, */
  2918. /*            and finds the length of the longest line            */
  2919. /******************************************************************/
  2920.  
  2921. {
  2922.     ULONG ulHashVal, ulHashValNoBlanks, ulRandom[512];
  2923.     BOOL fLastWasGraphic;
  2924.     USHORT usRandomIndex;
  2925.     char Char;
  2926.  
  2927. /* The hashing is done by setting up a table of pseudo-random numbers (the
  2928.    table is 512 long because PM won't print more characters than that, so
  2929.    we'll only pay attention to the first 512 chracters). The ASCII value of
  2930.    each character is multiplied by the next random number in the sequence,
  2931.    and added to the hash value for the line.
  2932.  
  2933.    Mathematical diversion:
  2934.    The random values and the hash values are 32 bits long; hopefully (but I'm
  2935.    not going to write a thesis to prove it) this will produce a distribution
  2936.    of hash values very close to perfect, in which case the chances of collision
  2937.    for N lines are given by: 1 - (2^32 - 1)! / ((2^32 - N)! * (2^32)^(N - 1) .
  2938.    (I think; my maths is getting weak, but it tallies with the case given in a
  2939.    text book of the chances of a shared birthday in a room of 23 people being
  2940.    0.5). This works out at 0.03 for the maximum N of 16384.
  2941.  
  2942.    The values for the pseudo-random sequence satisfy rules given in Kurth (Art
  2943.    of Computer Programming) for R(N) = (R(N-1) * A + C) mod M, i.e.
  2944.    1) C and M are relatively prime
  2945.    2) B = A - 1 is a multiple of every prime of M
  2946.    3) if M is a multiple of 4, B must be as well.
  2947.    Since M = 2^32, 2) is satisfied by satisfying 3), and 1) is satisfied by any
  2948.    odd number. This should give a sequence which only repeats every M values.
  2949.    To avoid 0 turning up in the ones used, I have started the sequence at C,
  2950.    i.e. the value following 0. Using B = 4 speeds up computation, and using a
  2951.    large C ensures that overflow will take place, which should help make the
  2952.    distribution of hash values even. */
  2953.  
  2954.     ulRandom[0] = 0xABCDEF01;
  2955.     for (usRandomIndex = 1; usRandomIndex < 512; usRandomIndex++)
  2956.     {
  2957.         ulRandom[usRandomIndex] = ulRandom[usRandomIndex - 1] * 5 + 0xABCDEF01;
  2958.     }
  2959.  
  2960.     while (ulChar < ulEnd && ltIndex <= 16384)
  2961.     {
  2962.         ulHashVal = 0;
  2963.         usRandomIndex = 0;
  2964.  
  2965.         ulLine[ltIndex] = ulChar;
  2966.  
  2967. /* White space is regarded as a space, null character or tab. Skip over any
  2968.    leading white space if required */
  2969.  
  2970.         if (uoUserOptions.fIgnoreLeadingBlanks == TRUE)
  2971.             while (ulChar < ulEnd &&
  2972.                    ((Char = CMEM (ulChar)) == ' ' ||
  2973.                      Char == '\0' ||
  2974.                      Char == '\t'))
  2975.                 ulChar++;
  2976.  
  2977.         fLastWasGraphic = TRUE;
  2978.  
  2979.         do
  2980.         {
  2981.             Char = CMEM (ulChar);
  2982.  
  2983. /* Skip all white space if required. Ignore characters if we have run out of
  2984.    random multipliers. */
  2985.  
  2986.             if ((!uoUserOptions.fIgnoreAllBlanks ||
  2987.                  (Char != ' ' &&
  2988.                   Char != '\0' &&
  2989.                   Char != '\t')) &&
  2990.                 usRandomIndex < 512)
  2991.             {
  2992.  
  2993. /* If ignoring trailing blanks, remember the last hash value from a non-blank
  2994.    character. */
  2995.  
  2996.                 if (uoUserOptions.fIgnoreTrailingBlanks == TRUE)
  2997.                 {
  2998.                     if (Char <= 32)
  2999.                     {
  3000.                         if (fLastWasGraphic == TRUE)
  3001.                         {
  3002.                             fLastWasGraphic = FALSE;
  3003.                             ulHashValNoBlanks = ulHashVal;
  3004.                         }
  3005.                     }
  3006.                     else
  3007.                         fLastWasGraphic = TRUE;
  3008.  
  3009.                 }
  3010.  
  3011.                 ulHashVal += ulRandom[usRandomIndex++] * Char;
  3012.             }
  3013.             ulChar++;
  3014.         } while (Char != '\n' && ulChar < ulEnd);
  3015.  
  3016.         if (uoUserOptions.fIgnoreTrailingBlanks && !uoUserOptions.fIgnoreAllBlanks)
  3017.             ulHashVal = ulHashValNoBlanks;
  3018.  
  3019.         if (Char == '\n')
  3020.         {
  3021.             sMaxLength = MAX (sMaxLength, (SHORT)(ulChar - ulLine[ltIndex]));
  3022.             ulLineHash[ltIndex] = ulHashVal;
  3023.             ltIndex++;
  3024.         }
  3025.     }
  3026.  
  3027.  
  3028.     return ltIndex - 1;
  3029.  
  3030. } /* end of MakeHashValues () */
  3031.  
  3032.  
  3033. void ChainIdenticalLines (void)
  3034. /**************************************************************************/
  3035. /* function : Finds lines with the same hash value, and forms them into a */
  3036. /* chain using the ltLinePair array as pointers. Also sets the foreground */
  3037. /* and background colours to the unmatched values.                        */
  3038. /**************************************************************************/
  3039.  
  3040. {
  3041.     SHORT htIndex, ltIndex, ltPair;
  3042.     USHORT usColBackgrnd;
  3043.     ULONG ulHashVal;
  3044.  
  3045. /* clear hash table */
  3046.     for (htIndex = 0; htIndex < sTotalLines; htIndex++)
  3047.         HashTable[htIndex] = 0;
  3048.  
  3049.     usColBackgrnd = COL_A_BGRND;
  3050.  
  3051. /* set up hash and line tables */
  3052.     for (ltIndex = 1; ltIndex <= sTotalLines; ltIndex++)
  3053.     {
  3054.         usBackCol[ltIndex] = usColBackgrnd;
  3055.         usLineCol[ltIndex] = COL_UNIQUE;
  3056.  
  3057.         ulHashVal = ulLineHash[ltIndex];
  3058.         htIndex = (SHORT) (ulHashVal % sTotalLines);
  3059.  
  3060. /* look for free place in hash table */
  3061.         while ((ltPair = HashTable[htIndex]) != 0
  3062.                && ulLineHash[ltPair] != ulHashVal)
  3063.             htIndex = (htIndex +1) % sTotalLines;
  3064.  
  3065. /* chain this line to any others with same hash value */
  3066.         ltLinePair[ltIndex] = ltPair;
  3067.  
  3068.         HashTable[htIndex] = ltIndex;
  3069.  
  3070. /* change background colour if starting on second file */
  3071.         if (ltIndex == sNumLines[0])
  3072.             usColBackgrnd = COL_B_BGRND;
  3073.     }
  3074.     return;
  3075. } /* end of ChainIdenticalLines () */
  3076.  
  3077.  
  3078. void FindUniquePairs (void)
  3079. /***************************************************************************/
  3080. /* function : pairs lines that occur exactly once in each file, and breaks */
  3081. /*            any other chains                                             */
  3082. /***************************************************************************/
  3083.  
  3084. {
  3085.     SHORT htIndex, ltIndex, ltPair;
  3086.     for (htIndex = 0; htIndex < sTotalLines; htIndex++)
  3087.     {
  3088.         if ((ltIndex = HashTable[htIndex]) == 0)
  3089. /* no such line */
  3090.             continue;
  3091.         if ((ltPair = ltLinePair[ltIndex]) != 0
  3092.             && ltLinePair[ltPair] == 0
  3093.             && ltIndex > sNumLines[0]
  3094.             && ltPair <= sNumLines[0])
  3095. /* one line in each file, so pair up */
  3096.             ltLinePair[ltPair] = ltIndex;
  3097.         else
  3098. /* identical lines in the same file, so break chain */
  3099.             while (ltIndex != 0)
  3100.             {
  3101.                 ltPair = ltLinePair[ltIndex];
  3102.                 ltLinePair[ltIndex] = 0;
  3103.                 ltIndex = ltPair;
  3104.             }
  3105.     }
  3106.     return;
  3107. } /* end of FindUniquePairs () */
  3108.  
  3109.  
  3110. void ExpandUniquePairs (void)
  3111. /*********************************************************************/
  3112. /* function : Pairs up identical lines before or after unique pairs. */
  3113. /*            These will be lines that have 2 or more duplicates.    */
  3114. /*********************************************************************/
  3115.  
  3116. {
  3117.     SHORT ltIndex, ltPair, i;
  3118.  
  3119.     for (ltIndex = 0; ltIndex <= sNumLines[0]+1; ltIndex++)
  3120.     {
  3121.  
  3122. /***************************************************************************/
  3123. /* pretend there are unique pairs before and after the files, so the first */
  3124. /* and last lines will be paired if identical                              */
  3125. /***************************************************************************/
  3126.         if (ltIndex == 0)
  3127.             ltPair = sNumLines[0];
  3128.         else if (ltIndex > sNumLines[0])
  3129.             ltPair = sTotalLines+1;
  3130.         else
  3131.             ltPair = ltLinePair[ltIndex];
  3132.  
  3133.         if (ltPair != 0)
  3134.         {
  3135. /* move forwards looking for identical lines */
  3136.             i = 1;
  3137.             while (ltPair + i <= sTotalLines
  3138.                    && ltIndex + i <= sNumLines[0]
  3139.                    && ulLineHash[ltIndex + i] == ulLineHash[ltPair + i]
  3140.                    && ltLinePair[ltIndex + i] == 0
  3141.                    && ltLinePair[ltPair + i] == 0)
  3142.             {
  3143.                 ltLinePair[ltIndex + i] = ltPair + i;
  3144.                 ltLinePair[ltPair + i] = ltIndex + i;
  3145.                 i++;
  3146.             }
  3147.  
  3148. /* and move backwards */
  3149.             i = -1;
  3150.             while (ltPair + i > sNumLines[0]
  3151.                    && ltIndex + i > 0
  3152.                    && ulLineHash[ltIndex + i] == ulLineHash[ltPair + i]
  3153.                    && ltLinePair[ltIndex + i] == 0
  3154.                    && ltLinePair[ltPair + i] == 0)
  3155.             {
  3156.                 ltLinePair[ltIndex + i] = ltPair + i;
  3157.                 ltLinePair[ltPair + i] = ltIndex + i;
  3158.                 i--;
  3159.             }
  3160.         }
  3161.     }
  3162.     return;
  3163. } /* end of ExpandUniquePairs () */
  3164.  
  3165.  
  3166. void SetColours (void)
  3167. /***************************************************************************/
  3168. /* function : Finds colours to display matching lines in, so that adjacent */
  3169. /*            blocks have different colours.                               */
  3170. /***************************************************************************/
  3171.  
  3172. {
  3173.     SHORT ltTopABlock, ltTopBBlock, ltBottomBBlock, ltIndex, ltPair, ltLoop;
  3174.     USHORT usColIndex;
  3175.  
  3176.     ltTopABlock = 0;
  3177.     ltTopBBlock = ltBottomBBlock = 0;
  3178.  
  3179.     for (ltIndex = 1; ltIndex <= sNumLines[0] + 1; ltIndex++)
  3180.     {
  3181.         if ((ltPair = ltLinePair[ltIndex]) == ltBottomBBlock+1)
  3182.             ltBottomBBlock = ltPair;    /* continuation of block */
  3183.         else
  3184.         {
  3185.             if (ltBottomBBlock != 0)    /* blocks to be coloured */
  3186.             {
  3187. /* find colour different from adjacent lines */
  3188.                 usColIndex = 0;
  3189.                 while (ltTopABlock > 1
  3190.                         && usLineCol[ltTopABlock - 1] == usColIndex
  3191.                        || ltTopBBlock > sNumLines[0] + 1
  3192.                         && usLineCol[ltTopBBlock - 1] == usColIndex
  3193.                        || ltBottomBBlock < sTotalLines
  3194.                         && usLineCol[ltBottomBBlock + 1] == usColIndex)
  3195.                     usColIndex++;
  3196.  
  3197. /* set colours in line table entries */
  3198.                 for (ltLoop = ltTopABlock; ltLoop < ltIndex; ltLoop++)
  3199.                     usLineCol[ltLoop] = usColIndex;
  3200.  
  3201.                 for (ltLoop = ltTopBBlock; ltLoop <= ltBottomBBlock; ltLoop++)
  3202.                     usLineCol[ltLoop] = usColIndex;
  3203.  
  3204. /* reset bottom of B block, i.e. no block current */
  3205.                 ltBottomBBlock = 0;
  3206.             }
  3207.  
  3208.             if (ltPair != 0)            /* new block */
  3209.             {
  3210.                 ltTopABlock = ltIndex;
  3211.                 ltTopBBlock = ltBottomBBlock = ltPair;
  3212.             }
  3213.         }
  3214.     }
  3215.     return;
  3216. } /* end of SetColours () */
  3217.  
  3218.  
  3219. void Composite (SHORT ltTopA, SHORT ltBottomA, SHORT ltTopB,
  3220.                 SHORT ltBottomB, SHORT ltPrecLine, SHORT ltFolLine)
  3221. /*****************************************************************************/
  3222. /* expected parameters: ltTopA, ltBottomA, ltTopB, ltBottomB = lines         */
  3223. /*                                   bounding section to be composed (input) */
  3224. /*                      ltPrecLine, ltFolLine = lines composite section      */
  3225. /*                                              is to be fitted into (input) */
  3226. /*                                                                           */
  3227. /* function : Finds the largest matching block of lines in the section given */
  3228. /*            to it, puts them in the composite file, and then calls itself  */
  3229. /*            to do the same for the lines above the matching section, and   */
  3230. /*            then those below it. If no matching sections are found, it     */
  3231. /*            puts in unmatched lines, or those which match with lines in    */
  3232. /*            another section.                                               */
  3233. /*****************************************************************************/
  3234.  
  3235. {
  3236.     SHORT sMaxBlockLength, sCurBlockLength, ltA, ltB,
  3237.           ltMaxBlockStart, ltMaxBlockStop, ltCurBlockStart, ltIndex;
  3238.     USHORT usCurCol;
  3239.  
  3240. /* find largest matching block */
  3241.     sMaxBlockLength = 0;
  3242.  
  3243.     for (ltIndex = ltTopA; ltIndex <= ltBottomA; ltIndex++)
  3244.     {
  3245.         if (ltLinePair[ltIndex] < ltTopB
  3246.             || ltLinePair[ltIndex] >ltBottomB)
  3247.             continue; /* no match, or matches outside this section */
  3248.  
  3249.         if (ltIndex == ltTopA
  3250.             || usLineCol[ltIndex] != usLineCol[ltIndex - 1])
  3251.         { /* start of matching block */
  3252.             sCurBlockLength = 1;
  3253.             ltCurBlockStart = ltIndex;
  3254.         }
  3255.  
  3256.         else
  3257.             sCurBlockLength++;
  3258.  
  3259.         if (sCurBlockLength > sMaxBlockLength)
  3260.         {
  3261.             sMaxBlockLength = sCurBlockLength;
  3262.             ltMaxBlockStart = ltCurBlockStart;
  3263.         }
  3264.     }
  3265.  
  3266.     if (sMaxBlockLength > 0)
  3267.     { /* matching block found */
  3268.         ltMaxBlockStop = ltMaxBlockStart + sMaxBlockLength - 1;
  3269.  
  3270. /* link lines in block, and change background colour to indicate they are
  3271.    matched lines. Increase count of number of lines in composite file.
  3272. */
  3273.         for (ltIndex = ltMaxBlockStart;
  3274.              ltIndex <= ltMaxBlockStop;
  3275.              ltIndex++)
  3276.         {
  3277.             if (ltIndex != ltMaxBlockStart)
  3278.                 ltLinePrev[ltIndex] = ltIndex - 1;
  3279.  
  3280.             if (ltIndex != ltMaxBlockStop)
  3281.                 ltLineNext[ltIndex] = ltIndex + 1;
  3282.  
  3283.             usBackCol[ltIndex] = usBackCol[ltLinePair[ltIndex]] = COL_MATCHED_BGRND;
  3284.  
  3285.             sNumLines[2]++;
  3286.         }
  3287.  
  3288. /* compose lines above matched section */
  3289.         Composite (ltTopA, ltMaxBlockStart - 1,
  3290.                    ltTopB, ltLinePair[ltMaxBlockStart] - 1,
  3291.                    ltPrecLine, ltMaxBlockStart);
  3292.  
  3293. /* now do those below it */
  3294.         Composite (ltMaxBlockStop + 1, ltBottomA,
  3295.                    ltLinePair[ltMaxBlockStop] + 1, ltBottomB,
  3296.                    ltMaxBlockStop, ltFolLine);
  3297.  
  3298.         return;
  3299.     }
  3300.     else
  3301.     { /* no matching lines found, so put in the odd ones */
  3302.         ltA = ltTopA;
  3303.         ltB = ltTopB;
  3304.  
  3305. /* do until all lines have been added */
  3306.         while (ltA <= ltBottomA || ltB <= ltBottomB)
  3307.         {
  3308.             if (uoUserOptions.fInterleaveUniqueLines)
  3309.             {
  3310. /* add any unique lines to the composite, alternately */
  3311.                 while (ltA <= ltBottomA
  3312.                        && ltB <= ltBottomB
  3313.                        && ltLinePair[ltA] == 0
  3314.                        && ltLinePair[ltB] == 0)
  3315.                 {
  3316.                     Add (<A, <PrecLine);
  3317.                     Add (<B, <PrecLine);
  3318.                 }
  3319.             }
  3320.  
  3321. /* add remaining unique lines from file A */
  3322.             while (ltA <= ltBottomA && ltLinePair[ltA] == 0)
  3323.                 Add (<A, <PrecLine);
  3324.  
  3325. /* and add those from file B */
  3326.             while (ltB <= ltBottomB && ltLinePair[ltB] == 0)
  3327.                 Add (<B, <PrecLine);
  3328.  
  3329.             if (ltA <= ltBottomA)
  3330.             {
  3331.                 usCurCol = usLineCol[ltA];
  3332.  
  3333. /* add any lines from file A that have matches, but not in this section */
  3334.                 while (ltA <= ltBottomA
  3335.                        && usLineCol[ltA] == usCurCol)
  3336.                     Add (<A, <PrecLine);
  3337.             }
  3338.  
  3339. /* and do the same for file B */
  3340.             if (ltB <= ltBottomB)
  3341.             {
  3342.                 usCurCol = usLineCol[ltB];
  3343.  
  3344.                 while (ltB <= ltBottomB
  3345.                        && usLineCol[ltB] == usCurCol)
  3346.                     Add (<B, <PrecLine);
  3347.             }
  3348.         }
  3349.  
  3350. /* link the lines to the next section of the composite file */
  3351.         ltLineNext[ltPrecLine] = ltFolLine;
  3352.         ltLinePrev[ltFolLine] = ltPrecLine;
  3353.     }
  3354.  
  3355.     return;
  3356. } /* end of Composite () */
  3357.  
  3358.  
  3359. void Add (PSHORT pltIndex, PSHORT pltPrecLine)
  3360. /* expected parameters: pltIndex = pointer to line table entry to be
  3361.                                 added to composite file (input/output)
  3362.                         pltPrecLine = preceding line in composite (input/output)
  3363. */
  3364.  
  3365. {
  3366.     ltLinePrev[*pltIndex] = *pltPrecLine;
  3367.     ltLineNext[*pltPrecLine] = *pltIndex;
  3368.  
  3369.     *pltPrecLine = *pltIndex;
  3370.     (*pltIndex)++;
  3371.  
  3372.     sNumLines[2]++;
  3373. } /* end of Add () */
  3374.  
  3375.  
  3376. PVOID AllocMem (USHORT usSize)
  3377. /* allocates memory for 2nd (non-PM) thread */
  3378.  
  3379. {
  3380.     SEL Selector;
  3381.  
  3382.     if (DosAllocSeg (usSize, &Selector, 0))
  3383.     {
  3384.         strcpy (pWriteLine, "can't allocate memory");
  3385.         return NULL;
  3386.     }
  3387.     return MAKEP (Selector, 0);
  3388.  
  3389. } /* end of AllocMem () */
  3390.